# NeuralRepo documentation --- --- > Idea manager with semantic search and duplicate detection. GitHub, Google or email-link sign-in; capture ideas from Claude (MCP), the nrepo CLI, the web app, the REST API or email, then tag, link and track them on a kanban board or mind map. --- --- # NeuralRepo Documentation Source: https://docs.neuralconfig.com/neuralrepo/ ## Capture from anywhere **Web App** Create and manage ideas from the dashboard with kanban boards and mind maps. **CLI** `nrepo push "idea"` — capture from your terminal in seconds. **Claude AI (MCP)** Save ideas directly from Claude conversations via the MCP protocol. **Siri Shortcuts** "Hey Siri, NeuralRepo" — capture by voice on iPhone, Mac, or Apple Watch. **Email** Mail an idea to your personal capture address from any device. **iOS App (not yet released)** A native app with a share sheet, a widget, and an offline capture queue, still in testing. ## Organize with intelligence **Semantic Search** Find ideas by meaning, not just keywords. Powered by vector embeddings. **Duplicate Detection** Automatic similarity detection catches duplicates before they pile up. **Idea Relations** Connect ideas with typed relations — blocks, inspires, parent, and more. **Mind Map** Visualize your idea graph as an interactive network. **Review Queue** A short daily queue: possible duplicates, new connections, and ideas that went quiet. ## Explore the docs - [Getting Started](https://docs.neuralconfig.com/neuralrepo/getting-started/introduction/): What NeuralRepo is, how it works, and what each plan includes. - [Capture Ideas](https://docs.neuralconfig.com/neuralrepo/capture/web/): Every way in — web, CLI, Claude, Siri, email, and the REST API, plus the iOS app still in testing. - [Organize & Explore](https://docs.neuralconfig.com/neuralrepo/organize/search/): Search, tags, statuses, relations, duplicates, the review queue, the mind map, and deleting data. - [AI Agent](https://docs.neuralconfig.com/neuralrepo/agent/overview/): The Pro agent that searches, links, and reorganizes your repo for you. - [CLI Reference](https://docs.neuralconfig.com/neuralrepo/cli/installation/): Full reference for all 20+ nrepo commands. - [API Reference](https://docs.neuralconfig.com/neuralrepo/api/overview/): REST API documentation with request/response examples. - [Integrations](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): Claude via MCP, Claude Code, BYOK providers, Apple Shortcuts, and the weekly digest. - [Claude Connector](https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/): Add NeuralRepo to Claude at https://neuralrepo.com/mcp/v2 and see what its 22 tools do on each plan. - [Guides](https://docs.neuralconfig.com/neuralrepo/guides/idea-to-project/): End-to-end workflows: idea to project, the idea graph, CI/CD, and migration. - [Architecture](https://docs.neuralconfig.com/neuralrepo/architecture/tech-stack/): How NeuralRepo is built on Cloudflare — data model, queues, search, and security. --- # Artifacts & Visualization Source: https://docs.neuralconfig.com/neuralrepo/agent/artifacts/ When the agent calls a tool, the result is rendered as a **visual artifact** rather than plain text. Artifacts give you interactive, scannable views of your ideas — and in graph mode, they animate directly onto the mind map. ## Artifact Types Six tools emit an artifact the canvas knows how to draw: | Type | Produced by | Description | |------|-------------|-------------| | `idea_grid` | `search_ideas` (browse or search) | Grid of idea cards with title, status, and tags | | `idea_card` | `get_idea`, `create_idea`, `update_idea` | Single idea, in detail / created / updated mode | | `mini_graph` | `discover_connections` | Small force graph showing newly discovered connections | | `brainstorm_cluster` | `brainstorm` | Clustered display of generated idea variations | | `tag_cloud` | `suggest_tags` | Tag suggestions with applied/unapplied state | | `summary` | `summarize` | Summary text with extracted themes | | `connection_line` | `link_ideas` | Animated edge drawn on the mind map (no canvas card) | **Note:** The canvas can also render `mermaid` diagrams, side-by-side `comparison` cards, and `insights` panels, but **no tool emits those types today** — they are renderers waiting for a producer. Likewise, `score_idea` and `draft_document` draw on the **mind map** (score cards over the graph, a new node for the document) and produce **no canvas artifact** in the Agent view; their results arrive as tool output there. ## How Artifacts Render ### The Agent View In the Agent view's split pane, artifacts appear in the **canvas panel** on the right. Each artifact is wrapped in a collapsible **artifact card** with: - A header showing the artifact type icon, a title, and collapse/pin/dismiss controls - A **pin** button (📌) to keep the artifact visible even as the conversation continues - A **dismiss** button (×) to remove the artifact from the canvas Multiple artifacts accumulate in the canvas as the conversation progresses. Pinned artifacts stay at the top; unpinned artifacts can be scrolled through. ### Mind Map Chat Panel In the mind map chat panel, artifacts render differently depending on their type: **Graph mutations** — tool calls that change the graph animate onto the canvas: | Artifact | Graph effect | |----------|-------------| | `connection_line` | New edge draws with animation between source and target nodes | | `idea_card` (created) | New node appears with a pulse animation | | `idea_card` (updated) | Existing node highlights briefly to show the change | | `mini_graph` | New nodes and edges for all discovered connections | **Non-graph artifacts** render as collapsible **ToolResultCards** in the chat message: - A single header line shows the tool icon and a summary (e.g., "Found 5 ideas for 'auth'") - Clicking the chevron expands to show idea reference chips, detail content, or a grid ### Idea Reference Chips When an artifact references specific ideas, they appear as **color-coded chips** with the idea title. Clicking a chip in the mind map chat panel highlights that node on the graph and scrolls it into view. ## Graph Mutations in Detail In the mind map, every tool result is translated into one or more **graph mutations** — the vocabulary the map understands: | Mutation | What happens | |----------|-------------| | `add_nodes` / `remove_nodes` | Adds or removes ideas on the graph (removal is how undo reverses a creation) | | `add_edges` / `remove_edges` | Draws or erases relations | | `highlight_nodes` | Pulses the named ideas in a colour, for a set duration | | `focus_node` / `focus_bbox` | Pans and zooms to one idea, or frames a whole set | | `focus_cluster` / `clear_focus_cluster` | Dims everything outside a set of ideas, then releases it | | `arrange_nodes` / `clear_arrangement` | Applies an agent-defined layout, or drops back to force layout | | `semantic_layout_weights` | Feeds pairwise similarities into the simulation so like clusters with like | | `show_score_cards` | Overlays `score_idea` results on the graph | | `show_tension_edges` | Draws red contradiction edges between opposing ideas | | `show_proposed_edges` | Draws suggested-but-not-yet-created connections | | `move_agent_cursor` / `hide_agent_cursor` | Walks a visible cursor across the graph while the agent works | Each tool has its own choreography — `search_ideas` highlights its hits and dims the rest, `discover_connections` adds edges then frames them, `create_idea` adds a node and highlights it in the accent colour. ## Undo Stack Every agent action that **writes** data is recorded in an **undo stack** in the chat panel. The undo button in the chat header reverses the most recent one: - `create_idea`, `synthesize_ideas`, `draft_document` — deletes the idea it created - `link_ideas`, `discover_connections` — removes the relations it created Layout changes are not undoable — clear an arrangement instead. The undo stack is per-session and cleared when the page reloads. **Tip:** Use undo liberally when exploring — the agent can create many connections quickly via `discover_connections`. If the results are not what you wanted, undo rolls back the graph to its previous state. ## ToolResultCard Non-graph artifacts in the mind map panel render using the `ToolResultCard` component. The card has: - **Icon** — a monospace emoji or symbol indicating the tool type - **Summary** — a single-line description of the result (e.g., "Merged 3 tags → machine-learning") - **Expand chevron** — click to reveal the full detail view - **Idea reference chips** — clickable chips for each idea referenced in the result The card uses the accent colour (Ember) for icons and a raised surface with a 1px border, in both the dark and light themes. ## Next Steps - [Agent Tools Reference](https://docs.neuralconfig.com/neuralrepo/agent/tools/): Every tool, its parameters, and the artifact or mutation it produces. - [AI Agent Overview](https://docs.neuralconfig.com/neuralrepo/agent/overview/): Models, code mode, graph mode, and the prompt palette. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): The canvas these mutations animate on — edges, filters, and force controls. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): What an agent-created edge means, and how it differs from one you drew. --- # AI Agent Overview Source: https://docs.neuralconfig.com/neuralrepo/agent/overview/ The NeuralRepo AI agent is a conversational assistant embedded directly in the web app. It can search your ideas, create new ones, discover connections, brainstorm, summarize, and perform advanced analysis — all through natural language, with results rendered as visual artifacts. **Caution:** The AI agent is a **Pro feature** — the WebSocket endpoint refuses free accounts outright, and the Agent view shows an upgrade panel instead. No API key of your own is needed: the two Workers AI models run on NeuralRepo's account. A BYOK Anthropic key unlocks the three Claude models on top of that. ## Two Interfaces The agent is available in two places: **Agent view** A full split-pane view with a chat panel on the left and an artifact canvas on the right. Open it from **Agent** in the sidebar. Best for deep exploration sessions where you want to see multiple artifacts side-by-side. **Mind Map Chat Panel** A floating, draggable chat panel that hovers over the mind map. Tool results animate directly onto the graph — nodes appear, edges draw, and clusters form in real time. Best for exploring your idea graph while conversing with the agent. ## Model Registry Five models are available. The three Anthropic models require a [BYOK key](https://docs.neuralconfig.com/neuralrepo/integrations/byok/); the two Workers AI models need no key at all. | Key | Label | Provider | BYOK | Context | Tool steps | Notes | |-----|-------|----------|:----:|---------|:----------:|-------| | `gpt-oss-120b` | GPT-OSS 120B | Workers AI | No | 128K | 12 | **The default.** Runs in code mode | | `qwen-30b` | Qwen 30B | Workers AI | No | 32K | 6 | Compact prompt; also the creative sub-agent | | `sonnet` | Sonnet | Anthropic | Yes | 1M | 10 | Balanced reasoning | | `haiku` | Haiku | Anthropic | Yes | 200K | 8 | Faster, lower cost | | `opus` | Opus | Anthropic | Yes | 1M | 10 | Most capable | The Anthropic keys are tiers, not fixed models: the agent uses the newest Sonnet, Haiku or Opus available, never older than `claude-sonnet-5`, `claude-haiku-4-5` or `claude-opus-5`. See [BYOK](https://docs.neuralconfig.com/neuralrepo/integrations/byok/#agent-model-registry) for how the model is chosen. Every model is capped at 8,192 output tokens except GPT-OSS 120B, which gets 16,384. "Tool steps" is the hard stop on how many tool calls the model may chain in one turn — a request that needs more than its budget stops mid-way rather than looping. Selecting an Anthropic model without a key configured returns a message telling you to pick a Workers AI model or add a key; it does not silently fall back. Your last-used model is remembered in `localStorage`. All Anthropic requests are routed through **Cloudflare AI Gateway** — providing logging, caching, and rate limiting without exposing your key to additional parties. ### Code Mode GPT-OSS 120B runs in **code mode** by default: instead of calling tools one at a time, it receives a single `codemode` tool and writes JavaScript that calls the tools as functions, executed in a sandboxed Worker. Multi-step work — search, filter, then link — becomes one round trip. You can force code mode on or off for any model with the `code_mode` user setting: ```bash curl -X PATCH https://neuralrepo.com/api/v1/user/me \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"settings_json": "{\"code_mode\": true}"}' ``` The tools available are identical either way — code mode only changes how the model reaches them. ## How It Works The agent is a **Cloudflare Durable Object** (`NeuralRepoAgent`) built on the [Agents SDK](https://developers.cloudflare.com/agents/) `AIChatAgent` class. It persists conversation history per user across sessions. ```mermaid flowchart TD browser["Browser"] -->|WebSocket| ws["/api/v1/agent/ws"] ws --> do["Durable Object
(NeuralRepoAgent)"] do --> stream["streamText
(Vercel AI SDK)"] stream -->|via AI Gateway| anthropic["Anthropic
(BYOK)"] stream -->|via AI binding| workersai["Workers AI"] ``` Messages stream back over the WebSocket connection. The frontend uses the Agents SDK client to manage the connection and reconstruct streaming state. ### Thinking Blocks Anthropic models run with **adaptive thinking**: the model decides for itself whether to reason before responding, and for how long. There is no fixed thinking budget. In the chat UI, a collapsible "Thought process" block appears above the response when thinking is active. While the model is still generating, an animated neural icon shows it is working. The Workers AI models do not produce thinking blocks. ### System Prompt Each conversation begins with a system prompt that includes: - The user's total idea count (active ideas only — archived ones are excluded) - 5 most recent ideas, with number, title, and status - Up to 20 tags with their idea counts, and a note of how many more exist Qwen 30B gets a **compact** version of that prompt — 3 recent ideas, 10 tags, and terser rules — because of its smaller context window. In Graph Mode (mind map chat panel), the system prompt also includes a note that the graph is the primary output, encouraging the agent to use tools aggressively and keep prose brief. ### Conversation History The Durable Object keeps your conversation across sessions. For models with a context window below 200K, history is trimmed from the **oldest** message forward until it fits the model's budget, so a long-running conversation quietly loses its beginning on Qwen 30B and GPT-OSS 120B while the Anthropic models keep everything. ## Graph Mode When using the chat panel embedded in the mind map, tool results animate directly onto the graph: - **New ideas** — nodes appear with a pulse animation - **New connections** — edges draw with an animation along the path - **Search results** — matching nodes highlight - **Semantic layout** — nodes re-cluster with a physics re-heat Every graph mutation is added to an **undo stack**. The undo button in the chat panel header reverses the last agent action (deleting created relations, removing created ideas, etc.). ## Working Memory Bar At the top of the Agent view's chat, a **working memory bar** shows a summary of what the agent has loaded in the current session: - Number of unique ideas referenced in artifacts - Number of connections made - Last search query This gives you a quick sense of the agent's context without scrolling through the conversation. ## Prompt Palette Both interfaces show a **prompt palette** — a set of contextual quick-action chips that update based on what you have selected or what the agent last did: - **Default chips** — "What am I working on?", "Find missing connections", "Weekly insights", "Surprise me" - **Single selection** — Expand, Find related, Brainstorm, Suggest tags, Summarize - **Multi-selection** — Connect these, Compare, Summarize cluster, Visualize - **Post-action** — context-specific follow-ups after brainstorm, search, scoring, etc. Click any chip to send that prompt immediately. ## Selection Awareness When you have nodes selected in the mind map, the agent automatically receives a list of selected idea IDs and titles as additional context. This allows prompts like "compare these" or "link these ideas" to work without you naming the ideas explicitly. --- # Agent Tools Reference Source: https://docs.neuralconfig.com/neuralrepo/agent/tools/ The agent's capabilities are exposed as **tools** — typed functions the model can call to interact with your idea repository. Tools are defined with Zod schemas and execute server-side with full database and Vectorize access. The reference below is split into two groups: twelve **core tools** that read and write your repository, and five **intelligence tools** that analyse it. Because the agent itself is Pro-only, **everyone who can open it has all seventeen** — the split is about what each tool is for, not about who gets it. **Note:** The agent selects tools automatically based on your request. You do not need to name tools explicitly — just describe what you want. Every idea the agent creates — with `create_idea`, `synthesize_ideas`, or `draft_document` — is recorded with source `api`, and goes through the same background processing as any other capture: embedding, duplicate detection, and auto-tagging. ## Core Tools ### `search_ideas` Search and browse ideas. With a query, performs semantic search (Vectorize embeddings + reranking). Without a query, lists ideas by recency. Supports status, tag, and limit filters. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `query` | string | No | Search query. Omit to browse all ideas. | | `status` | enum | No | Filter: `captured`, `exploring`, `building`, `shipped`, `shelved` | | `tag` | string | No | Filter by tag name | | `limit` | number | No | Max results — default **5** with a query, **20** while browsing; 100 is the ceiling | Search uses **semantic + FTS fallback**: Vectorize is queried first; if it returns no results above the score threshold, the tool falls back to SQLite FTS5 full-text search automatically. This is the agent's own behaviour — [the REST API and the CLI do not fall back](https://docs.neuralconfig.com/neuralrepo/organize/search/#default-behavior--and-where-the-fallback-actually-is). Filtering by a tag that does not exist returns an empty result with a hint naming the mistake, rather than silently returning everything. Returns an `idea_grid` artifact. --- ### `get_idea` Get full details of a single idea by ID, including tags, links, and relations. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `id` | number | Yes | Idea ID | | `include_related` | boolean | No | Include related ideas and links (default: true) | Returns an `idea_card` artifact. --- ### `create_idea` Create a new idea. Triggers background queue processing for embedding generation and auto-tagging. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `title` | string | Yes | Concise idea title | | `body` | string | No | Detailed description (Markdown) | | `tags` | string[] | No | Tags (lowercase, hyphenated) | | `status` | enum | No | Initial status (default: `captured`) | Returns an `idea_card` artifact. In graph mode, the new node appears on the mind map. --- ### `update_idea` Update an existing idea's title, body, status, or tags. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `id` | number | Yes | Idea ID to update | | `title` | string | No | New title | | `body` | string | No | New body | | `status` | enum | No | New status | | `tags` | string[] | No | Replace all tags with this list | | `add_tags` | string[] | No | Tags to add (cannot combine with `tags`) | | `remove_tags` | string[] | No | Tags to remove (cannot combine with `tags`) | Returns an `idea_card` artifact. Triggers re-embedding if title or body changed. --- ### `link_ideas` Create a typed relation between two ideas. Runs cycle detection before creating the relation. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `source_id` | number | Yes | Source idea ID | | `target_id` | number | Yes | Target idea ID | | `relation_type` | enum | No | `related` (default), `blocks`, `inspires`, `parent`, `supersedes` | | `note` | string | No | Optional note about the relationship (max 500 chars) | If `relation_type` is omitted, the agent selects the most appropriate type based on the ideas' content. `duplicate` is not offered — see [Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/). Cycle detection only *blocks* the hard-blocked types (`blocks`, `parent`); a `supersedes` cycle goes through. Linking an idea to itself, or re-linking a pair that is already connected, returns an error rather than a duplicate edge. Returns a `connection_line` artifact that animates on the mind map. --- ### `discover_connections` Find hidden connections between ideas using Vectorize semantic similarity. Scans ideas, identifies missing relations, and uses AI to reason about relationship types before creating them. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `scope` | string | No | `'all'` for all ideas, `'recent'` (default, last 7 days), or comma-separated idea IDs | | `min_score` | number | No | Minimum similarity score 0–1 (default 0.5) | Scope caps what is scanned: `'all'` takes your 100 most recent ideas, `'recent'` filters your 50 most recent down to those touched in the last 7 days — falling back to the 10 most recent if that window is empty. Pairs that already have a relation in either direction are skipped. The top 10 remaining candidates go to an AI pass that names the relation type; any it does not judge becomes `related`. The relations it writes are marked system-created, so the mind map draws them faintly. Returns a `mini_graph` artifact showing the new connections. --- ### `summarize` Summarize one or more ideas, extracting common themes and a connecting narrative. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_ids` | number[] | Yes | 1–20 idea IDs to summarize | | `write_back` | boolean | No | Save the summary to the idea's `source_summary` field (single idea only, default false) | Returns a `summary` artifact with the summary text and extracted themes. --- ### `brainstorm` Generate creative variations or extensions of an idea using the Qwen 30B sub-agent. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_id` | number | No | Base idea to brainstorm from | | `prompt` | string | No | Freeform brainstorm prompt | | `count` | number | No | Number of suggestions, 1–5 (default 3) | Either `idea_id` or `prompt` must be provided. Returns a `brainstorm_cluster` artifact. --- ### `suggest_tags` Get AI-suggested tags for an idea. Prefers existing user tags to avoid tag sprawl. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_id` | number | Yes | Idea to suggest tags for | Returns a `tag_cloud` artifact showing suggested tags with an indicator for which are already applied. --- ### `cleanup_tags` Find and merge similar or duplicate tags using tag embeddings. In dry-run mode (default), returns proposed merges for review. With `dry_run: false`, executes the merges. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `dry_run` | boolean | No | If true (default), show proposals without executing. If false, execute merges. | Uses a similarity threshold of 0.85. The most-used tag in each cluster becomes the canonical tag. Comparison needs tag embeddings, which are generated as tags are used, so a brand-new tag may be skipped on the first pass. --- ### `arrange_nodes` Arrange mind map nodes into visual clusters by tags, status, or custom groupings. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `mode` | enum | Yes | `by_tags`, `by_status`, or `by_clusters` | | `clusters` | object[] | No | Custom cluster definitions (required for `by_clusters` mode). Each object has `label` (string) and `ids` (number[]). | | `idea_ids` | number[] | No | Specific idea IDs to arrange (max 200). Omit to arrange all ideas. | Modes: - **`by_tags`** — groups ideas by shared tags - **`by_status`** — arranges ideas in status columns - **`by_clusters`** — uses custom groupings you define in the `clusters` parameter --- ### `clear_arrangement` Clear any agent-arranged node positions, letting nodes return to the natural force-directed layout. No parameters. --- ## Intelligence Tools Analysis rather than bookkeeping. Same availability as the core tools — the agent is Pro-only, so every session that can reach a tool can reach all of these. ### `semantic_layout` Rearrange the mind map so similar ideas cluster together. Computes pairwise vector similarities across up to 100 ideas and applies the weights as force-graph spring strengths. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_ids` | number[] | No | Specific idea IDs to lay out. Omit to use all visible ideas (up to 100). | Triggers a physics re-heat on the mind map. Semantically similar ideas pull toward each other; dissimilar ideas repel. --- ### `synthesize_ideas` Combine two ideas into a new synthesis idea, linked to both originals with `inspires` relations. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_id_1` | number | Yes | First idea ID | | `idea_id_2` | number | Yes | Second idea ID | | `synthesis_title` | string | No | Optional title. If omitted, AI generates one. | The synthesis body is AI-generated (2–3 sentences), capturing the best elements of both source ideas. The new idea is created with status `exploring`. --- ### `score_idea` Score ideas on 5 dimensions using AI evaluation. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_ids` | number[] | Yes | 1–10 idea IDs to score | **Scoring dimensions** (0–10 each), all produced by a single AI call: | Dimension | What it measures | |-----------|-----------------| | Novelty | How original and unique the idea is | | Feasibility | How practical and achievable it is | | Impact | How significant the potential impact could be | | Clarity | How well-defined and clearly articulated it is | | Momentum | How much energy and excitement the idea has | In the mind map, the scores render as cards over the graph and the scored ideas are framed. In the Agent view they arrive as tool output rather than a canvas artifact. **Caution:** If the model returns something the scorer cannot parse, every dimension falls back to **5/10** rather than reporting a failure. A set of ideas that all score a flat 5 across the board is that fallback, not a verdict — ask again. --- ### `find_contradictions` Detect idea pairs with opposing viewpoints or mutually exclusive approaches. Shows tension edges on the mind map. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `limit` | number | No | Max contradiction pairs to find, 1–20 (default 5) | Candidates come from your 100 most recent ideas, of which the newest 30 are compared against their nearest neighbours. Only pairs in a **0.6–0.95 similarity band** are considered — ideas too far apart to be about the same thing are ignored, and near-identical ones are duplicates rather than contradictions. The AI pass then keeps only pairs that are genuinely mutually exclusive, dropping anything it scores below 0.3. Returns contradiction pairs with AI-generated explanations and strength scores, drawn as red tension edges on the mind map. --- ### `draft_document` Generate a structured document from one or more ideas as source material. | Parameter | Type | Required | Description | |-----------|------|:--------:|-------------| | `idea_ids` | number[] | Yes | 1–20 idea IDs to use as source material | | `format` | enum | Yes | Document format | **Available formats:** | Format | Description | Outline structure | |--------|-------------|-------------------| | `prd` | Product Requirements Document | Overview, Problem, Goals, Requirements, Non-Goals, Success Metrics | | `blog_post` | Blog post | Title, Introduction, three main points, Conclusion | | `memo` | Internal memo | To/From/Subject/Date, Executive Summary, Key Points | | `research_brief` | Research brief | Background, Key Findings, Implications, Next Steps | | `feature_spec` | Feature specification | User Story, Acceptance Criteria, Technical Notes, Out of Scope | The generated document is saved as a new idea — status `exploring`, tagged `document`, and linked to every source idea with an `inspires` relation. In the mind map it appears as a new node; in the Agent view it arrives as tool output. Undo deletes the generated idea. --- ## Sub-Agents Several tools use lightweight **sub-agents** — secondary AI calls to a fixed Workers AI model — rather than the primary conversation model. This is why a tool behaves the same whichever model you have selected: | Sub-agent | Model | Used by | |-----------|-------|---------| | Brainstorm | Qwen 30B (creative) | `brainstorm` | | Contradiction detection | Qwen 30B (creative) | `find_contradictions` | | Document drafting | Qwen 30B (creative) | `draft_document` | | Summarize cluster | IBM Granite Micro (utility) | `summarize` | | Suggest tags | IBM Granite Micro (utility) | `suggest_tags` | | Find hidden connections | IBM Granite Micro (utility) | `discover_connections` | | Synthesis and scoring | IBM Granite Micro (utility) | `synthesize_ideas`, `score_idea` | Sub-agents are called server-side and their results are returned to the primary agent as tool output. They do not appear in the conversation history. ## Next Steps - [AI Agent Overview](https://docs.neuralconfig.com/neuralrepo/agent/overview/): Models, code mode, the system prompt, and how the agent is wired. - [Artifacts & Visualization](https://docs.neuralconfig.com/neuralrepo/agent/artifacts/): What each tool result looks like in the canvas and on the graph. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): The relation types link_ideas and discover_connections write. - [BYOK](https://docs.neuralconfig.com/neuralrepo/integrations/byok/): Add an Anthropic key to run these tools on Sonnet, Haiku, or Opus. --- # Authentication Source: https://docs.neuralconfig.com/neuralrepo/api/authentication/ All API endpoints (except `/api/v1/health`) require authentication. NeuralRepo accepts a session token, an API key, or a session cookie, and resolves them in a fixed order. ## How a request is authenticated Every `/api/v1/*` request runs through the same middleware chain before your endpoint sees it: ```mermaid flowchart TD start["Request to /api/v1/*"] --> bearer{"Authorization: Bearer present?"} bearer -- yes --> sess["Look up SHA-256 of the token
in sessions, unexpired only"] sess -- hit --> refresh["Session user
scopes = null, expiry pushed to +30 days"] sess -- miss --> keyb["Look up the same hash in api_keys"] keyb -- hit --> keyu["API-key user
scopes from the key row, last_used_at updated"] keyb -- miss --> xkey bearer -- no --> xkey{"X-API-Key starting with nrp_ ?"} xkey -- yes --> keyh["Look up SHA-256 of the key in api_keys"] keyh -- hit --> keyu keyh -- miss --> cookie xkey -- no --> cookie{"Cookie nrepo_session present?"} cookie -- yes --> cs["Look up SHA-256 in sessions
refresh expiry and re-set the cookie"] cs -- hit --> refresh cs -- miss --> deny["401 Unauthorized"] cookie -- no --> deny refresh --> plan["Load plan from users"] keyu --> plan plan --> rl["Rate limit
API-key and Bearer requests only"] rl --> route["Endpoint handler"] ``` In prose, so nothing here lives only in the diagram: 1. If an `Authorization: Bearer` header is present, its SHA-256 hash is looked up in the **sessions** table first, then in the **api\_keys** table. That is why a bearer token can be *either* credential. 2. Otherwise an `X-API-Key` header is tried, but **only if it starts with `nrp_`** — a key sent under that header without the prefix is ignored entirely. 3. Otherwise the `nrepo_session` cookie is tried. 4. If none matches, the request is rejected with `401` and the body `{ "error": "Unauthorized" }`. The message is the same whether the credential was missing, malformed, revoked, or expired. 5. A match sets the user and their plan; a session match also slides its expiry 30 days forward. 6. The rate limiter then runs, but only for API-key and bearer-token requests. ## Session Token When you sign in through the NeuralRepo web app, a session token is stored in a secure HTTP-only cookie. You can also pass it explicitly: ``` Authorization: Bearer ``` Sessions last **30 days** and slide: every authenticated request pushes the expiry another 30 days out, so an active session effectively never expires while a dormant one lapses after a month. Signing out via `/auth/logout` deletes the row immediately. **Note:** Session tokens are the credential the web app uses. For scripts, prefer an API key — cookie and bearer *session* auth are the two paths the rate limiter treats differently, and a session token pasted into a script is bearer auth, so it counts against your daily quota. ## API Key API keys provide long-lived access for scripts, CI pipelines, and third-party integrations. Every key is the prefix `nrp_` followed by 64 hexadecimal characters (32 random bytes): ``` X-API-Key: nrp_a1b2c3d4e5f6... ``` Keys do not expire. Only the SHA-256 hash is stored, so a lost key cannot be recovered — only revoked and replaced. ### Generating an API Key **Pro plan required:** `POST /user/api-keys` is gated on the Pro plan. On Free it returns `403` with `{ "pro_required": true, "feature": "API access" }` and no key is created. Signing in with `nrepo login` issues its own key labelled `CLI (auto-generated)` through the OAuth callback, which is a separate code path — see [CLI authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/). **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/user/api-keys \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"label": "CI Pipeline"}' ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/user/api-keys", { method: "POST", headers: { Authorization: "Bearer ", "Content-Type": "application/json", }, body: JSON.stringify({ label: "CI Pipeline" }), }); const key = await res.json(); console.log(key); ``` `label` is optional and defaults to `default`; it is capped at 100 characters. **Response `201 Created`** ```json { "id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "key": "nrp_a1b2c3d4e5f67890...", "label": "CI Pipeline", "created_at": "2026-03-24 12:00:00" } ``` The `id` is a 32-character hex string — it is what you pass to the delete endpoint, and it is not the key. :::caution The full key value is only returned once at creation time. Store it securely. ::: ### Listing Keys ``` GET /api/v1/user/api-keys ``` Returns every key on the account, newest first. The response carries **no key material at all** — not a masked value, not a prefix. Each entry is `id`, `label`, `scopes`, `source`, `last_used_at`, and `created_at`. `source` is `manual` for keys you created here and `mcp` for keys minted by the MCP OAuth flow. ### Revoking a Key ``` DELETE /api/v1/user/api-keys/:id ``` Deletes the row, so the key stops working on the next request. Returns `200 OK` with `{ "success": true }`, or `404` if the id does not belong to you. Unlike key creation, revoking is available on every plan. ## Scopes Scopes are stored per key and are **null for every key you create yourself**, which means full access. Only tokens minted by the MCP OAuth flow carry a scope string: | Scope | Granted to | Description | |-------|------------|-------------| | `ideas:read` | MCP OAuth tokens | Read ideas, tags, relations, links, search, map, duplicates | | `ideas:write` | MCP OAuth tokens | Create, update, archive, merge, and develop ideas; manage tags, links, and relations | A token whose scope string omits the scope an endpoint requires gets `403 { "error": "Insufficient scope: requires ideas:write" }`. Session tokens and manually created API keys never see that error, because they are unscoped. **Note:** This is the opposite of the usual arrangement: the *narrow* credential is the OAuth token an AI client holds, and the key you paste into a script is unrestricted. There is currently no way to create a scope-limited key from the dashboard or the API. ## MCP OAuth NeuralRepo issues MCP (Model Context Protocol) tokens for AI assistant integrations. The flow is separate from standard API authentication and is driven by the client, not by you: 1. You add `https://neuralrepo.com/mcp/v2` (or the original `https://neuralrepo.com/mcp/`) as an MCP server in a compatible client. 2. The client discovers the endpoints from `/.well-known/oauth-authorization-server`, registers itself, and walks the authorization-code + PKCE flow at `/mcp/authorize` and `/mcp/token`. PKCE is required for every new authorization, with `code_challenge_method=S256` stated explicitly; anything else gets `400` `invalid_request`. 3. The token it receives is written to the same `api_keys` table with `source = "mcp"`, a label of `MCP ()`, and the scopes you approved. `GET /api/v1/user/mcp-tokens` **lists** the tokens issued this way — it does not create one. Revoke them with the same `DELETE /api/v1/user/api-keys/:id` endpoint used for manual keys. ## Error Responses | Status | Body | Meaning | |--------|------|---------| | `401 Unauthorized` | `{ "error": "Unauthorized" }` | Missing, malformed, revoked, or expired credential | | `403 Forbidden` | `{ "error": "Insufficient scope: requires ideas:write" }` | Valid MCP token, wrong scope | | `403 Forbidden` | `{ "error": "API access requires a Pro plan", "pro_required": true, ... }` | Valid credential, plan gate | There is no distinct "invalid API key" message — every authentication failure returns the same `401` body, by design. ## Best Practices - Use **API keys** for server-side scripts and automations. - Use **session tokens** only from browser-based code. - Rotate API keys periodically and revoke unused keys — `last_used_at` on the list endpoint tells you which are dormant. - Never commit API keys to version control. ## Next Steps - [User & Settings](https://docs.neuralconfig.com/neuralrepo/api/user/): Manage keys, MCP tokens, BYOK provider keys, profile settings, and data exports. - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): Which credentials count against the daily quota and which are exempt. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): The full status-code vocabulary, including the two different 403 bodies. - [MCP Integration](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): Connect NeuralRepo to Claude and other MCP clients, and see which tools each scope unlocks. --- # Duplicates Source: https://docs.neuralconfig.com/neuralrepo/api/duplicates/ NeuralRepo compares each new idea against your existing ones and records pairs that score above your duplicate threshold. This API lists those detections and resolves them. **Free accounts always get an empty list:** `GET /ideas/duplicates` returns `{ "duplicates": [] }` for every Free account, before it touches the database — not because none were found. Detection keeps running in the background, so the backlog appears the moment you upgrade. On Free, an empty array proves nothing. ## How detections appear Detection is asynchronous and happens after capture, not during it: 1. `POST /ideas` returns immediately with `processing: true`. 2. The queue embeds the idea and compares it against your existing vectors. 3. Any pair scoring above `dedup_threshold` (default **0.75**) is written as a `pending` detection. Nothing in the create response tells you a duplicate was found — there is no synchronous warning and no blocking prompt. Poll this endpoint (or `GET /review`) a few seconds later instead. ## List Duplicates `GET /api/v1/ideas/duplicates` Returns every **pending** detection where both ideas are still unarchived, highest similarity first. Dismissed and merged detections are never returned, and there is no pagination. **curl** ```bash curl https://neuralrepo.com/api/v1/ideas/duplicates \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch( "https://neuralrepo.com/api/v1/ideas/duplicates", { headers: { "X-API-Key": "nrp_YOUR_KEY" } } ); const { duplicates } = await res.json(); ``` **Response `200 OK`** ```json { "duplicates": [ { "id": 7, "user_id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "idea_id": 158, "idea_number": 58, "idea_title": "Add SSO support for enterprise accounts", "idea_body": "Larger customers keep asking for SAML...", "idea_status": "captured", "idea_source": "cli", "idea_created_at": "2026-03-22 13:58:00", "duplicate_of_id": 137, "duplicate_number": 42, "duplicate_title": "Enterprise SSO integration", "duplicate_body": "SAML and OIDC for teams...", "duplicate_status": "exploring", "duplicate_source": "web", "duplicate_created_at": "2026-02-11 09:04:00", "similarity_score": 0.94, "status": "pending", "created_at": "2026-03-22 14:00:00" } ] } ``` | Field | Meaning | |---|---| | `id` | The **detection** id — this is what the dismiss and merge endpoints take | | `idea_id` | The idea whose capture or edit triggered the detection — normally the newer one | | `duplicate_of_id` | The older idea it matched | | `idea_body`, `duplicate_body` | First **280 characters** only, for previewing | | `similarity_score` | Cosine similarity, 0–1 | | `status` | Always `pending` in this listing | `idea_number` and `duplicate_number` are display numbers for showing the user; every write endpoint here takes the detection `id`, and the merge acts on the two idea `id`s. ## Dismiss Duplicate `POST /api/v1/ideas/duplicates/:id/dismiss` Marks the detection as `dismissed`. Both ideas are left exactly as they are, and the pair never appears in the list again. **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/ideas/duplicates/7/dismiss \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch( "https://neuralrepo.com/api/v1/ideas/duplicates/7/dismiss", { method: "POST", headers: { "X-API-Key": "nrp_YOUR_KEY" } } ); ``` **Response `200 OK`** ```json { "success": true } ``` Only a `pending` detection can be dismissed. Dismissing one that is already dismissed or merged returns `404 { "error": "Duplicate not found or already resolved" }` — the same body as an id that does not exist. ## Merge Duplicate `POST /api/v1/ideas/duplicates/:id/merge` Folds the two ideas together and marks the detection `merged`. **The newer idea survives, and relations do not move:** This merge keeps `idea_id` — the idea that *triggered* the detection — and archives `duplicate_of_id`, the older one. That is the opposite of what most people assume, and it is also the opposite survivor from [`POST /ideas/:id/merge`](https://docs.neuralconfig.com/neuralrepo/api/ideas/#merge-ideas), where you choose the keeper. Relations on the archived idea are **not** repointed here, so its connections are lost from the graph. ```bash curl -X POST https://neuralrepo.com/api/v1/ideas/duplicates/7/merge \ -H "X-API-Key: nrp_YOUR_KEY" ``` Exactly what happens, compared with the general merge: | Aspect | `duplicates/:id/merge` | `ideas/:id/merge` | |---|---|---| | Survivor | `idea_id` — the newer idea | The id in the path; you choose | | Body | Both joined with `\n\n---\n\n` | Both joined with `\n\n---\n\n` | | Tags | Union of both | Union of both | | `created_at` | The earlier of the two | The earlier of the two | | Relations | **Not transferred** | Repointed onto the survivor | | Links | Not transferred | Not transferred | | Loser's status | Unchanged, just archived | Set to `shelved`, then archived | | Detection row | Marked `merged` | Untouched | | Response | `{ "success": true }` | The surviving idea object | The loser is archived (you can [restore](https://docs.neuralconfig.com/neuralrepo/api/ideas/#restore-idea) it, but the merge itself isn't reversed), so pick deliberately: if the older idea is the one with the relations, the history, or the better title, dismiss the detection and use [`POST /ideas/:id/merge`](https://docs.neuralconfig.com/neuralrepo/api/ideas/#merge-ideas) instead, naming the keeper yourself. **Response `200 OK`** ```json { "success": true } ``` ## Workflow 1. **List** pending detections with `GET /ideas/duplicates`. 2. Compare `idea_title`/`idea_body` against `duplicate_title`/`duplicate_body`. 3. **Dismiss** false positives — nothing changes. 4. For true duplicates, decide which idea should survive: - the newer one → `POST /ideas/duplicates/:id/merge`; - the older one, or you want relations preserved → dismiss, then `POST /ideas/:id/merge` with the keeper in the path. Tune how many detections you get with the `dedup_threshold` [user setting](https://docs.neuralconfig.com/neuralrepo/api/user/#user-settings): raise it for fewer, stricter matches. ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Listed, dismissed, or merged | | `400 Bad Request` | `:id` is not a number | | `401 Unauthorized` | Missing or invalid auth | | `404 Not Found` | Detection not found, not yours, or already dismissed/merged | No endpoint here returns `403` on Free — the list simply comes back empty. ## Next Steps - [Duplicate detection concepts](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): How the threshold works, what the background pass compares, and how to tune it. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/api/review/): The same detections alongside stale ideas and fresh connections, in one call. - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/#merge-ideas): The general merge, where you choose which idea survives. - [User & Settings](https://docs.neuralconfig.com/neuralrepo/api/user/#user-settings): Set dedup_threshold and the other similarity thresholds. --- # Error Handling Source: https://docs.neuralconfig.com/neuralrepo/api/errors/ Every NeuralRepo API error returns a JSON object with an `error` field. The HTTP status gives the category; the body sometimes carries more. ## Error Response Format Most errors are a single string: ```json { "error": "Idea not found" } ``` **`error` is not always a string:** Validation failures on **ideas** and **links** return the validator's field map instead, so `error` is an *object*: ```json { "error": { "title": ["Too small: expected string to have >=1 characters"], "tags": ["Too big: expected array to have <=20 items"] } } ``` Code that does `console.error(error)` or string-concatenates it will print `[object Object]` and lose the reason. Test the type before formatting: ```js const msg = typeof error === "string" ? error : Object.entries(error).map(([f, msgs]) => `${f}: ${msgs.join(", ")}`).join("; "); ``` Tags and user/profile endpoints return the first validation message as a plain string, so the same request shape can give you either form depending on which resource you hit. Some errors add fields alongside `error`. Where they do, `error` may be a **machine-readable code** rather than a sentence — `POST /ideas` over the Free-plan cap returns `"error": "idea_limit_reached"` with the human-readable text in `message`. ## Status Codes ### `400 Bad Request` The path, body, or query parameters failed validation. ```json { "error": { "title": ["Too small: expected string to have >=1 characters"] } } ``` Common causes: - Missing required fields (`title` on idea creation, `q` on search) - A field over its limit (body > 50,000 characters, more than 20 tags, more than 50 bulk ids) - Invalid format (colour not `#rrggbb`, unparseable URL, unknown `mode` or `link_type`) - A non-numeric `:id` in the path — `{ "error": "Invalid idea ID" }` - **A relation that would create a cycle** — despite the wording of the message, this is a `400` ### `401 Unauthorized` Authentication is missing or invalid. The body is always identical: ```json { "error": "Unauthorized" } ``` There is no separate message for expired, revoked, malformed, or absent credentials — all four produce this exact response. Common causes: - No `Authorization`, `X-API-Key`, or cookie credential - An `X-API-Key` value that does not begin with `nrp_` (it is ignored entirely) - A session older than 30 days with no activity - A revoked key ### `403 Forbidden` Authenticated, but not allowed. Three distinct bodies: **Plan gate** — `POST /user/api-keys`, BYOK save/test, relation writes, agent WebSocket: ```json { "error": "Idea relations requires a Pro plan", "pro_required": true, "feature": "Idea relations", "upgrade_url": "https://neuralrepo.com/upgrade" } ``` **Free-plan idea cap** — `POST /ideas` only: ```json { "error": "idea_limit_reached", "message": "You've captured 50 ideas on the free plan. Upgrade to Pro for unlimited ideas.", "currentCount": 50, "limit": 50, "upgradeUrl": "https://neuralrepo.com/upgrade", "featureRequestUrl": "https://support.neuralrepo.com/feature-requests" } ``` **Scope** — MCP OAuth tokens only: ```json { "error": "Insufficient scope: requires ideas:write" } ``` Branch on `pro_required` and on `error === "idea_limit_reached"` rather than on the sentence. ### `404 Not Found` The resource does not exist, or belongs to another account — the two are deliberately indistinguishable. ```json { "error": "Idea not found" } ``` Common causes: - An id that is not yours - **Passing a display number (`#42`) where the API wants a database `id`** — the single most common cause of a spurious `404` - A duplicate detection that has already been dismissed or merged Note that some deletes do *not* 404 on a bad id: `DELETE /ideas/:id/links/:linkId` and `DELETE /map/relations/:id` both return `{ "success": true }` regardless. ### `409 Conflict` Only two situations produce a `409`: ```json { "error": "Tag name already exists" } ``` - `POST /tags` with a name you already use. - `POST /map/relations` when a user-created relation already exists between that pair in that direction — `{ "error": "Relation already exists" }`. Uniqueness ignores the relation type, so a second edge of a different type between the same two ideas also conflicts. **Caution:** A rejected cycle reads like a conflict and is not one — it comes back as `400`. Renaming a tag onto an existing name is not one either; that surfaces as a `500`. ### `429 Too Many Requests` The daily quota is exhausted. See [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/). ```json { "error": "Rate limit exceeded" } ``` **No `Retry-After` header is sent** — the response has no rate-limit headers of any kind. The window is a calendar day in UTC, so there is nothing to poll for until midnight. ### `500 Internal Server Error` An unhandled exception. In production the body is generic: ```json { "error": "Internal server error" } ``` The most reproducible cause is renaming a tag onto a name you already use — the unique constraint is not caught, so it surfaces here rather than as a `409`. ### `502 Bad Gateway` An upstream service the request depends on failed: | Endpoint | Cause | |---|---| | `POST /ideas/:id/develop` | Your BYOK provider rejected or failed the call; body includes `provider` | | `GET /user/support-link` | The support site could not mint a link | ### `503 Service Unavailable` Returned only by `GET /api/v1/health`, when the database probe fails: ```json { "status": "error", "error": "Database unavailable" } ``` ## Failures that are not error statuses Several operations report failure inside a `2xx` response. Checking `res.ok` is not enough: | Endpoint | Success status | What to check | |---|---|---| | `PATCH /ideas/bulk` | `200` | `errors` count and the per-idea `results` array | | `POST /map/relations` (bulk) | `201` | `errors` count and the per-link `results` array | | `POST /user/byok/:provider/test` | `200` | `ok` — a rejected key is still a `200` | | `GET /ideas/search` | `200` | `semantic_limit_reached`, and `search_type` if you assumed semantic | | `GET /ideas/duplicates` | `200` | On Free, the list is always empty regardless of what was detected | ## Handling Errors in Code **JavaScript** ```js function formatError(error) { if (typeof error === "string") return error; if (error && typeof error === "object") { return Object.entries(error) .map(([field, msgs]) => `${field}: ${[].concat(msgs).join(", ")}`) .join("; "); } return "Unknown error"; } const res = await fetch("https://neuralrepo.com/api/v1/ideas", { method: "POST", headers: { "X-API-Key": "nrp_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ title: "" }), }); if (!res.ok) { const body = await res.json(); switch (res.status) { case 400: console.error("Validation error:", formatError(body.error)); break; case 401: console.error("Auth error — check your API key"); break; case 403: console.error( body.error === "idea_limit_reached" ? body.message : body.pro_required ? `${body.feature} needs Pro: ${body.upgrade_url}` : formatError(body.error) ); break; case 429: // No Retry-After header exists — the quota resets at 00:00 UTC. console.error("Daily rate limit exhausted"); break; default: console.error(`Error ${res.status}:`, formatError(body.error)); } } ``` **curl** ```bash # curl shows the HTTP status with -w and the body together curl -s -w "\nHTTP Status: %{http_code}\n" \ -X POST https://neuralrepo.com/api/v1/ideas \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"title": ""}' ``` ## Summary Table | Status | Name | Retryable | `error` type | Description | |--------|------|-----------|--------------|-------------| | `400` | Bad Request | No | String or object | Fix the request and resend; includes refused cycles | | `401` | Unauthorized | No | String | Always `"Unauthorized"` | | `403` | Forbidden | No | String or code | Plan gate, idea cap, or insufficient scope | | `404` | Not Found | No | String | Wrong id — check you passed `id`, not `number` | | `409` | Conflict | No | String | Tag name taken, or that relation pair already exists | | `429` | Too Many Requests | Not today | String | Quota resets at 00:00 UTC; no `Retry-After` | | `500` | Internal Server Error | Sometimes | String | Retry once; a tag rename collision will never succeed | | `502` | Bad Gateway | Yes | String | Upstream AI provider or support service | | `503` | Service Unavailable | Yes | String | Health check only — the database is unreachable | ## Next Steps - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): What counts toward the daily quota, and why a 429 cannot be waited out. - [Authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): Why every auth failure looks the same, and how credentials are resolved. - [Overview](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers): The id-versus-number rule behind most unexpected 404s. - [Relations & Graph](https://docs.neuralconfig.com/neuralrepo/api/relations/): Cycle detection, the 409 pair rule, and bulk results that hide failures in a 201. --- # Ideas Source: https://docs.neuralconfig.com/neuralrepo/api/ideas/ Ideas are the core resource. Every endpoint on this page addresses an idea by its database `id`, never by the `#N` display number. **`:id` is the database id, not the number in your listings:** An idea carries both an `id` (unique across all accounts) and a `number` (a counter within your account). Listings, the CLI, and the web UI show the **number**; this API takes the **id**. Read `id` out of the response body — the two match only for the very first account ever created. See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). ## List Ideas `GET /api/v1/ideas` Returns unarchived ideas, newest first (`created_at` descending). ### Query Parameters | Parameter | Type | Description | |-----------|------|-------------| | `status` | string | One of `captured`, `exploring`, `building`, `shipped`, `shelved` | | `tag` | string | Filter by tag name (exact match) | | `connection_types` | string | Comma-separated relation types; returns ideas on either end of such a relation | | `limit` | number | Items per page (default `20`, clamped to `100`) | | `offset` | number | Items to skip (default `0`) | `status` combines with either filter. `tag` and `connection_types` do **not** combine — if both are supplied, `tag` wins and `connection_types` is ignored. **curl** ```bash curl "https://neuralrepo.com/api/v1/ideas?status=exploring&limit=10" \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch( "https://neuralrepo.com/api/v1/ideas?status=exploring&limit=10", { headers: { "X-API-Key": "nrp_YOUR_KEY" } } ); const { ideas, has_more } = await res.json(); ``` **Response `200 OK`** ```json { "ideas": [ { "id": 137, "number": 42, "title": "Add dark mode support", "body": "Users have requested a dark theme...", "status": "exploring", "source": "web", "source_url": null, "source_summary": null, "parent_id": null, "vectorize_id": "idea_137", "is_archived": 0, "tags": ["ui", "feature-request"], "created_at": "2026-03-20 10:00:00", "updated_at": "2026-03-22 14:30:00" } ], "has_more": true } ``` `has_more` is computed as "this page is exactly `limit` long", so the last full page reports `has_more: true` and the following request returns an empty array. Keep paging until `ideas` is empty rather than until `has_more` is `false`. ## Create Idea `POST /api/v1/ideas` ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | Yes | 1–200 characters | | `body` | string | No | Max 50,000 characters | | `tags` | string[] | No | Max 20 tags, each max 50 characters; unknown tags are created | | `source` | string | No | One of `web`, `cli`, `claude-mcp`, `siri`, `email`, `api`, `shortcut`, `ios` | | `source_url` | string | No | Valid URL, max 2,000 characters | | `status` | string | No | Initial status (default `captured`) | | `parent_id` | number | No | Parent idea **id** for nesting | **`source` defaults to `web`, not `api`:** The default is applied in the database layer and does not know which client called it. If you want your integration's ideas to be distinguishable — in the web UI's source icons, in CSV exports, or in your own reporting — send `"source": "api"` explicitly on every create. **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/ideas \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Implement webhook notifications", "body": "Send HTTP callbacks when ideas change status.", "tags": ["backend", "integrations"], "source": "api" }' ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/ideas", { method: "POST", headers: { "X-API-Key": "nrp_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ title: "Implement webhook notifications", body: "Send HTTP callbacks when ideas change status.", tags: ["backend", "integrations"], source: "api", }), }); const idea = await res.json(); ``` **Response `201 Created`** ```json { "id": 138, "number": 43, "title": "Implement webhook notifications", "body": "Send HTTP callbacks when ideas change status.", "status": "captured", "source": "api", "source_url": null, "parent_id": null, "vectorize_id": null, "is_archived": 0, "tags": ["backend", "integrations"], "processing": true, "created_at": "2026-03-24 09:00:00", "updated_at": "2026-03-24 09:00:00" } ``` `processing: true` means the idea is saved but its embedding, auto-tags, related-idea links, and duplicate check are still queued. `vectorize_id` stays `null` until that work completes — which is why a brand-new idea is not immediately findable by semantic search. If the queue write itself fails, the idea is still stored and an hourly recovery cron picks it up. ### Free-plan idea cap `POST /ideas` is the one endpoint with a hard plan limit. Once a Free account holds **50 unarchived ideas**, further creates are refused before validation: **Response `403 Forbidden`** ```json { "error": "idea_limit_reached", "message": "You've captured 50 ideas on the free plan. Upgrade to Pro for unlimited ideas.", "currentCount": 50, "limit": 50, "upgradeUrl": "https://neuralrepo.com/upgrade", "featureRequestUrl": "https://support.neuralrepo.com/feature-requests" } ``` Note that `error` here is a machine-readable code rather than a sentence — the human-readable text is in `message`. Archiving an idea frees a slot, since the count is of unarchived ideas. ## Get Idea `GET /api/v1/ideas/:id` Returns a single idea with its tags, links, and relations. Archived ideas are still returned by this endpoint — it is the one place they remain reachable. **Response `200 OK`** ```json { "id": 138, "number": 43, "title": "Implement webhook notifications", "body": "Send HTTP callbacks when ideas change status.", "status": "captured", "source": "api", "source_url": null, "parent_id": null, "is_archived": 0, "tags": ["backend", "integrations"], "links": [], "relations": [], "created_at": "2026-03-24 09:00:00", "updated_at": "2026-03-24 09:00:00" } ``` `relations` here is the **flat** list — every relation touching this idea in either direction, each carrying `related_idea_title` and `related_idea_number`, ordered by score. For the outgoing/incoming split, use [`GET /ideas/:id/relations`](https://docs.neuralconfig.com/neuralrepo/api/relations/#list-relations-for-an-idea). ## Update Idea `PATCH /api/v1/ideas/:id` All fields are optional. Only provided fields are updated. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | No | 1–200 characters | | `body` | string | No | Max 50,000 characters; replaces the body, never appends | | `status` | string | No | New status value | | `parent_id` | number \| null | No | Set or remove parent (pass `null` to detach) | | `tags` | string[] | No | **Replaces** all tags; max 20, each max 50 characters | Changing `title`, `body`, or `parent_id` re-queues the idea for embedding and relation reconciliation. Changing only `status` or `tags` queues a lighter metadata update and leaves the embedding alone. **Caution:** `is_archived` is not part of this schema. There is no field, on this endpoint or any other, that clears the archived flag — see [Delete Idea](#delete-idea) below. **Response `200 OK`** — the updated idea, with its tags. ## Bulk Update Ideas `PATCH /api/v1/ideas/bulk` Update status and/or tags for up to 50 ideas in one request. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `ids` | number[] | Yes | Idea **ids** to update, 1–50 | | `status` | string | No | New status for all ideas | | `tags` | string[] | No | Replace all tags (cannot be combined with `add_tags`/`remove_tags`) | | `add_tags` | string[] | No | Append tags without removing existing ones | | `remove_tags` | string[] | No | Remove specific tags | At least one of `status`, `tags`, `add_tags`, or `remove_tags` must be provided. Combining `tags` with either of the other two returns `400`. **This endpoint is not atomic:** Ideas are processed one at a time, and a failure on one does not stop or roll back the others. A response with `errors > 0` means the batch partially applied. **Read the `results` array** — the HTTP status is `200` even when every single idea failed. **curl** ```bash curl -X PATCH https://neuralrepo.com/api/v1/ideas/bulk \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "ids": [137, 138, 96], "status": "building", "add_tags": ["sprint-1"] }' ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/ideas/bulk", { method: "PATCH", headers: { "X-API-Key": "nrp_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ ids: [137, 138, 96], status: "building", add_tags: ["sprint-1"], }), }); const { updated, errors, results } = await res.json(); if (errors) console.warn(results.filter((r) => r.status === "error")); ``` **Response `200 OK`** ```json { "updated": 2, "errors": 1, "results": [ { "id": 137, "status": "updated" }, { "id": 138, "status": "updated" }, { "id": 96, "status": "error", "error": "Idea 96 not found" } ] } ``` An id that does not exist — or belongs to another account — comes back as a per-idea `error` row, not a `404`. ## Delete Idea `DELETE /api/v1/ideas/:id` Archives the idea by setting `is_archived = 1`, then deletes its vector from the search index. **Archiving can be undone:** `is_archived` is absent from the update schema; [Restore Idea](#restore-idea) is the only way back. The embedding is deleted rather than detached, so a restored idea is re-indexed. The row survives until you [permanently delete it](#permanently-delete-archived-ideas). What an archived idea disappears from: | Surface | Effect | |---|---| | `GET /ideas`, `GET /map`, `GET /review` | Excluded | | `GET /ideas/search` | Excluded from both keyword and semantic results | | Relations | Filtered out of every relation query, in both directions | | `GET /ideas/duplicates` | Detections referencing it are dropped | | `POST /user/export` and the CSV/Markdown exports | Excluded — archiving before exporting loses the content | | `GET /ideas/:id` | Still returns it, if you kept the id | | Free-plan idea cap | Frees a slot; the cap counts unarchived ideas only | **Response `200 OK`** — `{ "success": true }`. A `404` means the id is not yours or does not exist; deleting an already-archived idea succeeds again and changes nothing. ## Restore Idea `POST /api/v1/ideas/:id/restore` Un-archives the idea and queues it to be re-embedded for semantic search. No request body. Restoring an idea that is already active changes nothing and returns it as is. **Response `200 OK`** — the idea object with its `tags`, as in [Get Idea](#get-idea) but without `links` and `relations`. | Status | When | |---|---| | `400` | The id is not a number | | `403` | Free plan with 50 active ideas already: the same `idea_limit_reached` body as [Create Idea](#free-plan-idea-cap). An MCP token without `ideas:write` gets `Insufficient scope`. | | `404` | The id is not yours or does not exist | ## List Archived Ideas `GET /api/v1/ideas/archived` Returns your archived ideas, most recently updated first. It is the list that permanent deletion works from. ```json { "ideas": [ { "id": 391, "number": 42, "title": "Rewrite auth flow", "updated_at": "2026-09-20 14:02:11" } ], "count": 1 } ``` ## Permanently Delete Archived Ideas Two endpoints remove archived ideas for good. Each deleted idea takes its tags, links, relations, duplicate records and search vector with it. A tag that no remaining idea uses is deleted with its vector. A tag still in use has its vector re-embedded, through the background queue, from the ideas that remain. Idea vectors are deleted before the idea rows, so if the search index fails, nothing is deleted and a retry finds the same ideas. Vectors of orphaned tags are deleted after the idea rows, just before those tag rows. | Endpoint | Deletes | |---|---| | `DELETE /api/v1/ideas/:id/permanent` | One idea, which must already be archived | | `DELETE /api/v1/ideas/archived` | Every archived idea you have (the web app's **Settings ▸ Delete** button) | **Web session only:** Both endpoints refuse API keys and MCP tokens, whatever their scopes, with `403` `Sign in on the web to permanently delete ideas`. Only a signed-in browser session can delete data irreversibly. See [Delete Data](https://docs.neuralconfig.com/neuralrepo/organize/delete-data/) for the web flow. **Response `200 OK`:** ```json { "success": true, "ideas_deleted": 1, "idea_vectors_deleted": 1, "tags_deleted": 0, "tag_vectors_deleted": 0, "tags_reembed_queued": 2 } ``` | Status | Body `error` | When | |---|---|---| | `400` | `Invalid idea ID` | `:id` is not a number | | `403` | `Sign in on the web to permanently delete ideas` | Called with an API key or MCP token | | `404` | `Idea not found` | The id is not yours or does not exist | | `409` | `Only archived ideas can be permanently deleted. Archive it first.` | The idea is live — archive it with `DELETE /ideas/:id` first | `DELETE /ideas/archived` with nothing archived returns `200` with every count at `0`. ## Merge Ideas `POST /api/v1/ideas/:id/merge` Folds another idea into this one. The idea in the path (`keep_id`) survives. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `absorb_id` | number | Yes | **id** of the idea to absorb | ```bash curl -X POST https://neuralrepo.com/api/v1/ideas/137/merge \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"absorb_id": 138}' ``` Exactly what happens: | Aspect | Behaviour | |---|---| | Body | Both bodies joined with a `\n\n---\n\n` separator, kept idea first | | Tags | Union of both sets | | Relations | Repointed from the absorbed idea to the kept idea; self-relations are removed | | Links | **Not transferred** — they stay on the absorbed idea, which is then archived | | `created_at` | The **earlier** of the two, so the merged idea keeps the original capture date | | Absorbed idea | Set to `status: "shelved"` **and** archived — and therefore not recoverable | | `supersedes` relation | Not created; archived ideas are filtered from relation queries, so it would be invisible | Merging an idea with itself returns `400`. If either id is missing or not yours, the whole operation returns `404` and nothing is changed. **Response `200 OK`** — the surviving idea, with its merged tag list. **Note:** Resolving a duplicate detection is a **different** merge with different rules — it keeps the newer idea and does not move relations. See [Duplicates](https://docs.neuralconfig.com/neuralrepo/api/duplicates/#merge-duplicate). ## Develop Idea `POST /api/v1/ideas/:id/develop` Sends the idea, its tags, its related ideas, and its links to your configured AI provider and asks for a buildable specification. **This endpoint writes to the idea:** The generated spec is not just returned — it is appended to the idea's `body` under an `## AI-Generated Spec` heading, separated by `---`. Calling it twice appends twice. Save the original body first if you need to compare. Requires a [BYOK provider key](https://docs.neuralconfig.com/neuralrepo/integrations/byok/). Storing one is itself a Pro feature, so in practice this endpoint is Pro-only. ```bash curl -X POST https://neuralrepo.com/api/v1/ideas/137/develop \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "spec": "### Problem\n\n...", "provider": "anthropic", "model": "claude-sonnet-5-5", "usage": { "input_tokens": 2400, "output_tokens": 1800 } } ``` The response is capped at 4,096 output tokens and the prompt asks for under 1,500 words, laid out as Problem / Proposed solution / Key features / Technical approach / Open questions — with a Mermaid diagram in the technical section where one helps. | Failure | Status | Body | |---|---|---| | No provider key stored | `400` | `{ "error": "No AI provider configured...", "needs_byok": true }` | | Provider rejected the call or returned an error | `502` | `{ "error": "", "provider": "anthropic" }` | | Idea id not found | `404` | `{ "error": "Idea not found" }` | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Successful read, update, archive, merge, or develop | | `201 Created` | Idea created | | `400 Bad Request` | Validation error, non-numeric `:id`, self-merge, or a bad bulk payload | | `401 Unauthorized` | Missing or invalid auth | | `403 Forbidden` | Free-plan idea cap reached (`idea_limit_reached`) | | `404 Not Found` | Idea not found, or not yours | | `502 Bad Gateway` | Your AI provider failed during `develop` | No endpoint on this page returns `409`. ## Next Steps - [Search](https://docs.neuralconfig.com/neuralrepo/api/search/): Find ideas by keyword or meaning, and what the Free-plan semantic allowance changes. - [Relations & Graph](https://docs.neuralconfig.com/neuralrepo/api/relations/): Link ideas to each other, in bulk, with cycle detection on the directional types. - [Duplicates](https://docs.neuralconfig.com/neuralrepo/api/duplicates/): Review detections and merge them — with the other merge's rules. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): The two shapes an error body can take, and what each status code really means. --- # Links Source: https://docs.neuralconfig.com/neuralrepo/api/links/ Links attach external URLs to an idea — reference material, GitHub issues, design documents, or any relevant resource. All three endpoints take the idea's database **id** in the path, not its `#N` display number. **Connecting two ideas is a relation, not a link:** A link on this page is a URL on a single idea. A connection **between two ideas** (`related`, `parent`, `blocks`, `inspires`, `supersedes`) is a **relation**: create it with `POST /api/v1/map/relations`, which accepts up to 50 at once, or with `nrepo link` (`--batch` reads many from stdin). Creating relations is a **Pro** feature and returns `403` on the Free plan; reading them works on every plan. See [Relations](https://docs.neuralconfig.com/neuralrepo/api/relations/). ## List Links `GET /api/v1/ideas/:id/links` Returns every link attached to the idea, newest first. Available on all plans. **curl** ```bash curl https://neuralrepo.com/api/v1/ideas/137/links \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch( "https://neuralrepo.com/api/v1/ideas/137/links", { headers: { "X-API-Key": "nrp_YOUR_KEY" } } ); const { links } = await res.json(); ``` **Response `200 OK`** ```json { "links": [ { "id": 1, "idea_id": 137, "url": "https://github.com/org/repo/issues/42", "title": "GitHub Issue #42", "link_type": "github-issue", "created_at": "2026-03-20 10:00:00" } ] } ``` The same links are also embedded in the `links` array of `GET /ideas/:id`, so fetching an idea already gives you them — this endpoint exists for when you only need the links. ## Add Link `POST /api/v1/ideas/:id/links` ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `url` | string | Yes | Must parse as a URL; max 2,000 characters | | `title` | string | No | Display title, max 200 characters | | `link_type` | string | No | One of the five types below (default `url`) | **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/ideas/137/links \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://github.com/org/repo", "title": "Project repository", "link_type": "github-repo" }' ``` **JavaScript** ```js const res = await fetch( "https://neuralrepo.com/api/v1/ideas/137/links", { method: "POST", headers: { "X-API-Key": "nrp_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://github.com/org/repo", title: "Project repository", link_type: "github-repo", }), } ); const { link } = await res.json(); ``` **Response `201 Created`** ```json { "link": { "id": 2, "idea_id": 137, "url": "https://github.com/org/repo", "title": "Project repository", "link_type": "github-repo", "created_at": "2026-03-24 09:00:00" } } ``` Nothing is fetched, validated, or unfurled at the far end: the URL only has to parse. A link to a page that 404s is stored exactly like one that works, and `title` is whatever you supply — it is never read from the page. Duplicate URLs on the same idea are allowed and create separate rows. ## Delete Link `DELETE /api/v1/ideas/:id/links/:linkId` Removes a link from an idea. Both ids are required, and the link must belong to that idea. ```bash curl -X DELETE https://neuralrepo.com/api/v1/ideas/137/links/2 \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "success": true } ``` **A wrong link id still reports success:** Only the **idea** is checked for existence and ownership. If the idea is yours, the delete returns `{ "success": true }` whether or not `:linkId` matched anything. Confirm removal by re-listing rather than by the status code. ## Link Types `link_type` is a closed set — any other value is rejected with `400`: | Type | Description | |------|-------------| | `url` | General web link (default) | | `claude-chat` | Claude conversation link | | `github-repo` | GitHub repository | | `github-issue` | GitHub issue or PR | | `attachment` | Accepted, but see below | **`attachment` is a label, not a feature:** The value is accepted and stored, but nothing in NeuralRepo produces or treats it specially — there is no file-upload endpoint, and a link you tag `attachment` is stored and rendered exactly like a `url`. Use it as your own marker if it helps; do not expect a file to be hosted. ## Link Schema | Field | Type | Description | |-------|------|-------------| | `id` | number | Unique link identifier — the `:linkId` for deletion | | `idea_id` | number | The idea this link belongs to | | `url` | string | The linked URL | | `title` | string \| null | Display title | | `link_type` | string | One of the five types above | | `created_at` | string | UTC `YYYY-MM-DD HH:MM:SS` | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Links listed, or delete accepted | | `201 Created` | Link added | | `400 Bad Request` | Non-numeric id, unparseable URL, title too long, or unknown `link_type` | | `401 Unauthorized` | Missing or invalid auth | | `404 Not Found` | The **idea** does not exist or is not yours | ## Next Steps - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/): Fetch an idea with its links, tags, and relations in one request. - [Relations & Graph](https://docs.neuralconfig.com/neuralrepo/api/relations/): Link ideas to each other rather than to external URLs. - [Claude Code integration](https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/): Where claude-chat links come from, and what NeuralRepo does with them. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): Every status code the API returns and how to tell them apart. --- # Mind Map Source: https://docs.neuralconfig.com/neuralrepo/api/map/ The mind map endpoint returns all of your unarchived ideas and their relations as a graph structure, ready for visualization. It is available on every plan. ## Get Map Data `GET /api/v1/map` Returns the complete graph: nodes (ideas) and edges (relations). There is no pagination and no `limit` — you always get the whole graph, optionally narrowed by capture date. ### Query Parameters | Parameter | Type | Description | |-----------|------|-------------| | `since` | string | Only nodes whose `created_at` is at or after this value | | `until` | string | Only nodes whose `created_at` is at or before this value | Both are compared as strings against the stored `YYYY-MM-DD HH:MM:SS` timestamps, so `?since=2026-03-01` works and an ISO string with a `Z` will not match the way you expect. **Note:** The window filters **nodes only**. Edges are fetched for the whole account, so a narrowed window can return edges whose endpoints are not in `nodes`. Drop any edge whose `source` or `target` is missing from the node set before rendering. **curl** ```bash curl "https://neuralrepo.com/api/v1/map?since=2026-03-01" \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/map", { headers: { "X-API-Key": "nrp_YOUR_KEY" }, }); const { nodes, edges } = await res.json(); ``` **Response `200 OK`** ```json { "nodes": [ { "id": 137, "number": 42, "title": "Add dark mode support", "status": "exploring", "tags": ["ui", "feature-request"], "connectionCount": 2, "created_at": "2026-03-20 10:00:00" }, { "id": 138, "number": 43, "title": "Theme customization options", "status": "building", "tags": ["ui"], "connectionCount": 1, "created_at": "2026-03-18 08:00:00" } ], "edges": [ { "id": 1, "source": 137, "target": 138, "type": "related", "score": 0.87, "note": "Both deal with theming", "created_by": "system", "created_at": "2026-03-20 10:00:00" }, { "id": 2, "source": 96, "target": 137, "type": "blocks", "score": null, "note": null, "created_by": "user", "created_at": "2026-03-19 08:00:00" } ] } ``` Nodes come back newest first; edges are ordered by descending `score`, which puts the system-detected similarities before the ones you drew by hand. Ideas with no relations are included, with `connectionCount: 0`. ## Node Schema | Field | Type | Description | |-------|------|-------------| | `id` | number | Idea **id** — what edges reference and what every other endpoint takes | | `number` | number | The `#N` display number, for labelling only | | `title` | string | Idea title | | `status` | string | `captured`, `exploring`, `building`, `shipped`, or `shelved` | | `tags` | string[] | Associated tag names | | `connectionCount` | number | Edges touching this node, counted from the edge list in this response | | `created_at` | string | UTC `YYYY-MM-DD HH:MM:SS` | ## Edge Schema | Field | Type | Description | |-------|------|-------------| | `id` | number | Relation identifier — pass this to `PATCH`/`DELETE /map/relations/:id` | | `source` | number | Source idea id | | `target` | number | Target idea id | | `type` | string | `related`, `parent`, `blocks`, `inspires`, `supersedes`, or `duplicate` | | `score` | number \| null | Similarity score if auto-detected, `null` if you created it | | `note` | string \| null | Optional description | | `created_by` | string | `system` for auto-detected edges, `user` for yours | | `created_at` | string | UTC `YYYY-MM-DD HH:MM:SS` | **Caution:** The relation type arrives as `type` on an edge, but as `relation_type` everywhere else in the API. Both name the same field in the database. ## Archived ideas An archived idea is dropped from `nodes`, and any edge with an archived idea at **either** end is dropped from `edges`. Since archiving cannot be undone, an idea that vanishes from your map is gone from it permanently. ## Managing Relations The map endpoint is read-only. To create, update, or delete relations, use the [Relations](https://docs.neuralconfig.com/neuralrepo/api/relations/) endpoints — all three of which require Pro: - `POST /api/v1/map/relations` — create one relation, or up to 50 at once - `PATCH /api/v1/map/relations/:id` — update a relation - `DELETE /api/v1/map/relations/:id` — delete a relation ## Usage Tips - Use the node `tags` and `status` fields to filter or colour-code nodes. - Style edges by `type` (dashed for `related`, solid arrows for `blocks`) and by `created_by`, so a reader can tell suggested connections from deliberate ones. - `connectionCount` is precomputed from this same response — sizing nodes by it needs no extra pass. ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Graph data returned | | `401 Unauthorized` | Missing or invalid auth | ## Next Steps - [Relations & Graph](https://docs.neuralconfig.com/neuralrepo/api/relations/): Create, update, and delete the edges this endpoint returns. - [Mind map concepts](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): How the in-app map lays the graph out and what each edge colour means. - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/): Fetch the full record behind a node, including its body and links. - [Overview](https://docs.neuralconfig.com/neuralrepo/api/overview/): Pagination, timestamp format, and the id-versus-number rule that governs every node. --- # API Overview Source: https://docs.neuralconfig.com/neuralrepo/api/overview/ The NeuralRepo API is a RESTful JSON API that lets you programmatically manage ideas, tags, relations, and user settings. Everything the web app, the CLI, and the MCP server do is done through these endpoints. ## Base URL All API requests are made to: ``` https://neuralrepo.com/api/v1 ``` The API is versioned through the URL path. The current (and only) version is **v1**. ## Content Type All request and response bodies use JSON. Set the `Content-Type` header on every request that includes a body: ``` Content-Type: application/json ``` ## Authentication Every endpoint under `/api/v1` except `/api/v1/health` requires authentication. Three credentials are accepted: | Method | Header | Format | |--------|--------|--------| | API key | `X-API-Key` | `nrp_` + 64 hexadecimal characters | | API key or session token | `Authorization` | `Bearer ` | | Session cookie | `Cookie` | `nrepo_session=` — set by the web app | The `Authorization: Bearer` header accepts **either** a session token or an API key; the server tries the session table first and falls back to the API-key table. See [Authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/) for the exact resolution order, key generation, and scopes. ## Idea identifiers Every idea carries **two** integers, and they are not interchangeable: | Field | What it is | Where you see it | |---|---|---| | `id` | Database primary key, unique across all accounts | Every `:id` path segment in this API | | `number` | Per-account counter assigned at capture time | The `#42` in CLI listings and the web UI | **Every endpoint in this API keys off `id`.** Nothing resolves a `number`, so passing the `#42` you read in a listing either returns someone else's idea number range as a `404` or — worse — silently addresses a *different* idea of yours. **Never pass a display number to this API:** `GET /api/v1/ideas/42` fetches the idea whose **`id`** is 42, not the one shown as `#42`. The two values coincide only for the very first account ever created. Read `id` out of the response body and pass that. ## Pagination Pagination is **not** uniform across list endpoints — check the endpoint's own reference page: | Endpoint | `limit` | `offset` | Notes | |---|---|---|---| | `GET /ideas` | default `20`, max `100` | default `0` | `has_more` is `true` when the page is exactly `limit` long | | `GET /ideas/search` | default `20`, max `100` | not supported | Keyword mode can return up to **2×** `limit` | | `GET /tags` | not supported | not supported | Returns every tag | | `GET /ideas/duplicates` | not supported | not supported | Returns every pending detection | | `GET /map` | not supported | not supported | Returns the whole graph; filter with `since`/`until` | | `GET /review` | not supported | not supported | Returns the whole queue | Example: ``` GET /api/v1/ideas?limit=50&offset=100 ``` ## Response Format Successful responses return the resource wrapped in a named object (`{ "ideas": [...] }`, `{ "tags": [...] }`, `{ "relation": {...} }`). Delete operations return `{ "success": true }`. Error responses carry an `error` field — but note that on validation failures **`error` is an object, not a string**. See [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/). ### Timestamps Every `created_at` and `updated_at` value is a **UTC SQLite timestamp**, not ISO-8601: ```json { "created_at": "2026-03-24 09:00:00" } ``` There is no `T` separator and no `Z` suffix. `new Date("2026-03-24 09:00:00")` parses as *local* time in most runtimes, so append `Z` yourself before parsing. ## Rate Limiting Requests authenticated with an API key or bearer token are rate-limited per user and per day — 100/day on Free, 10,000/day on Pro. Cookie-authenticated requests from the web app are exempt. Exceeding the limit returns `429`, and **no `Retry-After` header is sent**. See [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/). ## Health Check The health endpoint is the only unauthenticated route under `/api/v1`. Use it to verify the API is reachable: **curl** ```bash curl https://neuralrepo.com/api/v1/health ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/health"); const data = await res.json(); console.log(data); ``` **Response `200 OK`** ```json { "status": "ok", "environment": "production", "timestamp": "2026-03-25T12:00:00.000Z" } ``` The health check runs a `SELECT 1` against the database. If that query fails, the endpoint returns `503 Service Unavailable` with `{ "status": "error", "error": "Database unavailable" }`. **Note:** `timestamp` here *is* an ISO-8601 string — it is generated in the Worker rather than read from the database. Resource timestamps follow the SQLite format described above. ## Field Limits All input fields are validated against these maximum lengths: | Field | Max | Behaviour when exceeded | |-------|-----|-------------------------| | Idea title | 200 characters | `400` | | Idea body | 50,000 characters | `400` | | Tags per idea | 20 | `400` | | Tag name | 50 characters | `400` | | Source URL | 2,000 characters | `400` | | Display name | 100 characters | `400` | | API key label | 100 characters | `400` | | Settings JSON | 10,000 characters | `400` | | Search query | 500 characters | `400` | | List limit | 100 (default 20) | Clamped to 100, no error | | Relation note | 500 characters | **Silently truncated**, no error | | Bulk idea update | 50 ids | `400` | | Bulk relation create | 50 links | `400` | **Caution:** Two of these do not behave like the rest. A `limit` above 100 is clamped rather than rejected, and a relation `note` longer than 500 characters is stored truncated — the response reports success and you lose the tail. ## Plan gates Some endpoints require a Pro plan and return `403` with `pro_required: true` on Free: | Endpoint | Gate | |---|---| | `POST /user/api-keys` | Pro | | `PUT`/`POST` on `/user/byok/:provider` | Pro | | `POST`, `PATCH`, `DELETE` on `/map/relations` | Pro | | `GET /agent/ws` | Pro | Two more limits are enforced by returning *fewer results* rather than an error, which is easy to mistake for a bug: - `GET /ideas/duplicates` returns `{ "duplicates": [] }` for every Free account. - `GET /ideas/search` gives Free accounts 10 semantic searches per month, then quietly serves keyword results. And one is a hard cap on writes: `POST /ideas` returns `403 idea_limit_reached` once a Free account holds 50 unarchived ideas. ## Versioning The API is currently at **v1**. Breaking changes will be introduced under a new version prefix (e.g., `/api/v2`). Non-breaking additions (new fields, new optional parameters) may be added to v1 at any time. ## Endpoint index | Resource | Endpoints | |----------|-----------| | [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/) | `GET` `POST` `/ideas`, `GET` `PATCH` `DELETE` `/ideas/:id`, `PATCH /ideas/bulk`, `POST /ideas/:id/merge`, `POST /ideas/:id/develop` | | [Search](https://docs.neuralconfig.com/neuralrepo/api/search/) | `GET /ideas/search` | | [Duplicates](https://docs.neuralconfig.com/neuralrepo/api/duplicates/) | `GET /ideas/duplicates`, `POST /ideas/duplicates/:id/dismiss`, `POST /ideas/duplicates/:id/merge` | | [Tags](https://docs.neuralconfig.com/neuralrepo/api/tags/) | `GET` `POST` `/tags`, `PATCH` `DELETE` `/tags/:id`, `GET /tags/similar` | | [Links](https://docs.neuralconfig.com/neuralrepo/api/links/) | `GET` `POST` `/ideas/:id/links`, `DELETE /ideas/:id/links/:linkId` | | [Relations](https://docs.neuralconfig.com/neuralrepo/api/relations/) | `GET /ideas/:id/relations`, `POST /map/relations`, `PATCH` `DELETE` `/map/relations/:id` | | [Mind Map](https://docs.neuralconfig.com/neuralrepo/api/map/) | `GET /map` | | [Review Queue](https://docs.neuralconfig.com/neuralrepo/api/review/) | `GET /review`, `POST /review/:ideaId/keep` | | [User & Settings](https://docs.neuralconfig.com/neuralrepo/api/user/) | `GET` `PATCH` `/user/me`, API keys, MCP tokens, BYOK, shortcuts, support link | | [Export](https://docs.neuralconfig.com/neuralrepo/api/user/#data-export) | `POST /user/export`, `/user/export/csv`, `/user/export/markdown` | Three further routes exist under `/api/v1` but are not part of the documented REST surface: `GET /agent/ws` is a WebSocket upgrade used by the web app's [agent chat](https://docs.neuralconfig.com/neuralrepo/agent/overview/), `POST /user/billing/checkout` and `/user/billing/portal` return single-use Stripe URLs for the web app's upgrade flow, and `/api/v1/admin/*` is operator-only. ## Next Steps - [Authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): Generate an API key, understand scopes, and see the exact order credentials are resolved in. - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/): The core resource — create, read, update, archive, merge, and develop ideas. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): Every status code the API returns, the two shapes the error field can take, and what is retryable. - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): Daily quotas per plan, what counts against them, and why there is no Retry-After header. --- # Rate Limits Source: https://docs.neuralconfig.com/neuralrepo/api/rate-limits/ NeuralRepo enforces a per-user daily request limit on programmatic access. The limit applies only to requests authenticated with an **API key or a bearer token**. Requests carrying only the `nrepo_session` cookie — that is, the web app — are exempt, because a single page load makes many calls and the quota exists for scripted access. ## Daily Limits | Plan | Limit | |------|-------| | Free | 100 requests per day | | Pro | 10,000 requests per day | The counter is keyed on your user id and the **UTC date**, so it resets at 00:00 UTC. It is a single quota per account: every API key, MCP token, and bearer session you hold draws from the same bucket. ## What counts | Request | Counted? | |---|---| | Any `/api/v1/*` call with `X-API-Key` | Yes | | Any `/api/v1/*` call with `Authorization: Bearer` | Yes — session tokens included | | Any `/api/v1/*` call with only the session cookie | No | | `GET /api/v1/health` | No — it runs before the middleware chain | | A request that then fails with `400`/`404` | Yes — the counter increments before your handler runs | | A request rejected with `429` | No — the counter is already at the limit and is not raised further | Reads count exactly as much as writes. There is no separate write quota. ## Retry Behavior When you exceed your daily limit, the API responds with `429 Too Many Requests`: ```json { "error": "Rate limit exceeded" } ``` **There is no Retry-After header:** The `429` response carries no `Retry-After`, no `X-RateLimit-Remaining`, and no reset timestamp — reading them returns `null`. The only thing you know from a `429` is that you are done until 00:00 UTC. Because the window is a whole day, retrying within the same session will not succeed. Back off so that a transient failure does not turn into a retry storm, but treat a sustained `429` as a signal to stop for the day rather than to keep trying. ### Example: Retry with Backoff ```js async function fetchWithRetry(url, options, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const res = await fetch(url, options); if (res.status !== 429) return res; // No Retry-After is sent — back off on a fixed schedule. const delay = Math.pow(2, attempt) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); } throw new Error("Rate limit exceeded after retries"); } ``` ## Limits that are not rate limits Three other caps are easy to mistake for rate limiting, because two of them return `200` with less data rather than an error: | Cap | Plan | What you see | |---|---|---| | 50 unarchived ideas | Free | `POST /ideas` returns `403 idea_limit_reached` | | 10 semantic searches per month | Free | `GET /ideas/search` returns keyword results with `semantic_limit_reached: true` | | Duplicate detections hidden | Free | `GET /ideas/duplicates` returns `{ "duplicates": [] }` | The semantic-search counter is monthly (not daily) and is consumed *before* the search runs, so a search that fails or matches nothing still spends one of the ten. ## Best Practices - **Batch operations.** `PATCH /ideas/bulk` updates up to 50 ideas for one request, and `POST /map/relations` accepts up to 50 links in one call. - **Cache responses** when possible to reduce request volume. - **Spread requests evenly** across the day rather than sending bursts. - **Watch your own count.** Nothing in the response tells you how much quota is left, so track request counts client-side if you are near the Free limit. ## Status Codes | Status | Meaning | |--------|---------| | `429 Too Many Requests` | Daily quota exhausted; resets at 00:00 UTC | ## Next Steps - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/): Bulk endpoints that let you do more per request, and the Free-plan idea cap. - [Search](https://docs.neuralconfig.com/neuralrepo/api/search/): How the monthly semantic-search allowance behaves once it runs out. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): Every status code the API returns and which of them are worth retrying. - [Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): What Free and Pro include, including trials. --- # Relations & Graph Source: https://docs.neuralconfig.com/neuralrepo/api/relations/ Relations connect ideas to each other, forming a navigable graph. Each relation has a source, a target, a type, an optional note, and a `created_by` marker recording whether you made it or NeuralRepo's similarity pass did. **Writing relations is a Pro feature:** `POST`, `PATCH`, and `DELETE` on `/map/relations` all return `403` with `pro_required: true` on the Free plan. **Reading** relations — `GET /ideas/:id/relations` and `GET /map` — works on every plan, so a Free account can see the connections the system found for it but cannot add its own. ## List Relations for an Idea `GET /api/v1/ideas/:id/relations` Returns relations involving the idea, split by direction. Relations to or from an archived idea are omitted from both lists. **curl** ```bash curl https://neuralrepo.com/api/v1/ideas/137/relations \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch( "https://neuralrepo.com/api/v1/ideas/137/relations", { headers: { "X-API-Key": "nrp_YOUR_KEY" } } ); const { outgoing, incoming } = await res.json(); ``` **Response `200 OK`** ```json { "outgoing": [ { "id": 1, "relation_type": "related", "score": 0.87, "note": "Both deal with theming", "created_by": "system", "created_at": "2026-03-20 10:00:00", "idea_id": 138, "idea_number": 43, "idea_title": "Theme customization options", "idea_status": "building" } ], "incoming": [ { "id": 2, "relation_type": "blocks", "score": null, "note": null, "created_by": "user", "created_at": "2026-03-19 08:00:00", "idea_id": 96, "idea_number": 12, "idea_title": "Implement design tokens", "idea_status": "captured" } ] } ``` The row is **flat** — the other side of the relation arrives as `idea_id`, `idea_number`, `idea_title`, and `idea_status` rather than as a nested object, and it carries no tags. `id` is the relation's own id, which is what `PATCH` and `DELETE` take. Rows are ordered by `relation_type`, then by descending `score`. ## Create Relation `POST /api/v1/map/relations` Pro only. Accepts a single relation, or up to 50 at once. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `source_idea_id` | number | Yes | **id** of the source idea | | `target_idea_id` | number | Yes | **id** of the target idea | | `relation_type` | string | No | `related` (default), `parent`, `blocks`, `inspires`, or `supersedes` | | `note` | string | No | Free text; anything past 500 characters is silently truncated | **Note:** `duplicate` is a valid stored relation type but **cannot be created through this endpoint** — it is rejected as invalid. It exists for detections written by the duplicate pass. See [Duplicates](https://docs.neuralconfig.com/neuralrepo/api/duplicates/). **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/map/relations \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "source_idea_id": 137, "target_idea_id": 138, "relation_type": "related", "note": "Both deal with theming" }' ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/map/relations", { method: "POST", headers: { "X-API-Key": "nrp_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ source_idea_id: 137, target_idea_id: 138, relation_type: "related", note: "Both deal with theming", }), }); const { relation } = await res.json(); ``` **Response `201 Created`** ```json { "relation": { "id": 17, "source_idea_id": 137, "target_idea_id": 138, "relation_type": "related", "note": "Both deal with theming" } } ``` The single-relation response carries no `created_at` and no `score` — `score` is only ever set by the automatic similarity pass, never on a relation you create. Re-read the relation through `GET /ideas/:id/relations` if you need those fields. ### Creating many at once Send a `links` array instead of a single body. Up to **50** per call. ```bash curl -X POST https://neuralrepo.com/api/v1/map/relations \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "links": [ { "source_idea_id": 137, "target_idea_id": 138, "relation_type": "related" }, { "source_idea_id": 137, "target_idea_id": 96, "relation_type": "blocks", "note": "needs tokens first" } ] }' ``` **Response `201 Created`** ```json { "linked": 1, "errors": 1, "results": [ { "source_idea_id": 137, "target_idea_id": 138, "relation_type": "related", "status": "created", "relation_id": 17 }, { "source_idea_id": 137, "target_idea_id": 96, "relation_type": "blocks", "status": "error", "error": "This would create a circular blocking chain: #137 → #96 → #137" } ] } ``` **Bulk mode always returns 201, even when nothing was created:** Every failure — a missing idea, a cycle, a duplicate pair, an invalid type — is reported as a `status: "error"` row inside a `201` response. There is no rollback and no error status code. Check `errors` and read `results`. ## Update Relation `PATCH /api/v1/map/relations/:id` Pro only. Changes the type, the note, or both. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `relation_type` | string | No | `related`, `parent`, `blocks`, `inspires`, or `supersedes` | | `note` | string \| null | No | Updated note; `null` clears it, over 500 characters truncates | ```bash curl -X PATCH https://neuralrepo.com/api/v1/map/relations/17 \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"relation_type": "blocks"}' ``` A body with neither field returns `400 Nothing to update`. **Updating skips cycle detection entirely:** The cycle checks described below run on **create only**. Changing an existing `related` edge to `blocks` or `parent` can therefore produce exactly the cycle that a fresh create would have refused. If you rely on the graph being acyclic, delete and re-create rather than patching the type. **Response `200 OK`** — the updated relation wrapped in `{ "relation": {...} }`, or `404` if the relation id is not yours. ## Delete Relation `DELETE /api/v1/map/relations/:id` Pro only. ```bash curl -X DELETE https://neuralrepo.com/api/v1/map/relations/17 \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "success": true } ``` Deleting is idempotent and unverified: an id that does not exist, or belongs to another account, still returns `{ "success": true }`. It never returns `404`. ## Duplicate pairs The uniqueness rule is `(source_idea_id, target_idea_id, created_by)` — it does **not** include the relation type. So: - A second user-created relation from A → B is refused with `Relation already exists`, even if you ask for a different type. Change the existing one with `PATCH` instead. - A → B and B → A are two different pairs, and both may exist. - A system-created relation between the same two ideas does not block your user-created one; they coexist, and both appear in the graph. That refusal is the one error on this page that really does return `409`. ## Cycle Detection Creating a relation runs a breadth-first walk from the proposed target, following edges of the **same type**, looking for a path back to the source. What happens then depends on the type: | Types | Cycle behaviour | |---|---| | `blocks`, `parent` | **Hard block** — always refused; `?force=true` does not help | | `supersedes` | **Soft block** — refused, but `?force=true` creates it anyway | | `related`, `inspires` | Not checked at all — the walk is skipped | The rejection message names the whole chain: ```json { "error": "This would create a circular blocking chain: #137 → #96 → #137" } ``` For `supersedes` the same message ends with `Use ?force=true to bypass.` **A rejected cycle returns 400, not 409:** Despite the wording, the API classifies this as a validation failure: the response status is `400 Bad Request`. Match on the status **and** the message, or you will treat a cycle as a generic bad request. In bulk mode it is not a status at all — just an `error` row. Two more properties of the check: - The numbers in the message are **database ids** with a `#` prefix, even though `#N` means the display number everywhere else in NeuralRepo. They are ids. - The walk stops after 50 hops. A cycle that only closes beyond that depth is not detected and the relation is created. ```bash # Soft block: allowed through with ?force=true curl -X POST "https://neuralrepo.com/api/v1/map/relations?force=true" \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "source_idea_id": 138, "target_idea_id": 137, "relation_type": "supersedes" }' ``` ## Relation Schema | Field | Type | Description | |-------|------|-------------| | `id` | number | Relation identifier — what `PATCH`/`DELETE` take | | `source_idea_id` | number | Source idea **id** | | `target_idea_id` | number | Target idea **id** | | `relation_type` | string | `related`, `parent`, `blocks`, `inspires`, `supersedes`, or `duplicate` (system only) | | `score` | number \| null | Similarity score for system-created relations; `null` for yours | | `note` | string \| null | Optional description, stored truncated at 500 characters | | `created_by` | string | `user` or `system` | | `created_at` | string | UTC `YYYY-MM-DD HH:MM:SS` | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Read, update, or delete succeeded | | `201 Created` | Relation created — **including bulk calls where every link failed** | | `400 Bad Request` | Validation error, self-link, unknown relation type, or a refused cycle | | `401 Unauthorized` | Missing or invalid auth | | `403 Forbidden` | Free plan — writing relations requires Pro | | `404 Not Found` | An idea id in the request does not exist, or the relation id does not (`PATCH` only) | | `409 Conflict` | A user-created relation already exists between that pair, in that direction | ## Next Steps - [Mind Map](https://docs.neuralconfig.com/neuralrepo/api/map/): Fetch every node and edge at once for visualization. - [Relations concepts](https://docs.neuralconfig.com/neuralrepo/organize/relations/): What each relation type means, and which ones the similarity pass creates for you. - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/#merge-ideas): Merging repoints relations — see exactly how before you fold two ideas together. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): Which failures are statuses and which are rows inside a 2xx response. --- # Review Queue Source: https://docs.neuralconfig.com/neuralrepo/api/review/ The review endpoints back the Review tab: one call that returns everything worth revisiting — ideas that have gone quiet, connections found for you recently, and duplicate detections waiting on a decision. ## Get the Review Queue `GET /api/v1/review` Returns all three groups plus their counts. Available on every plan, with one difference noted below. **curl** ```bash curl https://neuralrepo.com/api/v1/review \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/review", { headers: { "X-API-Key": "nrp_YOUR_KEY" }, }); const { stale, connections, duplicates, counts } = await res.json(); ``` **Response `200 OK`** ```json { "stale": [ { "id": 96, "number": 12, "title": "Implement design tokens", "body": "Ship a token layer before theming...", "status": "captured", "source": "web", "updated_at": "2026-01-04 11:12:00", "days_stale": 79 } ], "connections": [ { "id": 31, "relation_type": "related", "score": 0.71, "created_by": "system", "created_at": "2026-03-22 06:00:00", "source_idea_id": 137, "source_number": 42, "source_title": "Add dark mode support", "target_idea_id": 138, "target_number": 43, "target_title": "Theme customization options" } ], "duplicates": [], "counts": { "stale": 1, "connections": 1, "duplicates": 0, "total": 2 }, "stale_threshold_days": 30 } ``` | Group | What it contains | |---|---| | `stale` | Ideas still in `captured` whose `updated_at` is older than `stale_threshold_days`, oldest first, with `days_stale` precomputed | | `connections` | Relations created in the **last 7 days**, newest first, with both endpoints' numbers and titles and a `created_by` telling you whether the system proposed it or you drew it | | `duplicates` | Pending duplicate detections — the same rows as [`GET /ideas/duplicates`](https://docs.neuralconfig.com/neuralrepo/api/duplicates/) | | `counts` | Sizes of each group and their `total`, for a badge | | `stale_threshold_days` | The threshold actually applied, so you can label the list | **`stale` and `connections` are capped at 20 each:** Both lists stop at 20 rows, and there is no parameter to raise it or to page past it. `counts` is computed from the returned arrays, so `counts.stale: 20` means "20 or more", not "20". Use [`GET /ideas?status=captured`](https://docs.neuralconfig.com/neuralrepo/api/ideas/) if you need the full backlog. `stale_threshold_days` comes from your `stale_threshold_days` [user setting](https://docs.neuralconfig.com/neuralrepo/api/user/#user-settings) and defaults to **30**. The connections window is fixed at 7 days and is not configurable. **Duplicates are empty on Free:** The `duplicates` group follows the same rule as the duplicates endpoint: Free accounts always get an empty array, and `counts.duplicates` is `0`. The other two groups are unaffected, so a Free account still gets a useful queue — just never a duplicate in it. Only `captured` ideas can be stale. An idea moved to `exploring`, `building`, `shipped`, or `shelved` leaves the stale list permanently, whatever its date. ## Keep an Idea `POST /api/v1/review/:ideaId/keep` "Still relevant." Bumps the idea's `updated_at` to now, which drops it out of the stale group for another `stale_threshold_days`. Nothing else changes — not the status, not the body, not the tags. ```bash curl -X POST https://neuralrepo.com/api/v1/review/96/keep \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "success": true } ``` **Note:** Like the other touch-only endpoints, this one does not verify the idea exists: an id that is not yours still returns `{ "success": true }` and changes nothing. Only a non-numeric `:ideaId` is rejected, with `400`. ## Resolving the rest of the queue Only "keep" is a review-specific action. Everything else reuses the endpoints you already have: | Item | Action | Endpoint | |---|---|---| | Stale idea | Still relevant | `POST /review/:ideaId/keep` | | Stale idea | Move it along | `PATCH /ideas/:id` with a new `status` | | Stale idea | Let it go | `DELETE /ideas/:id` — archives, permanently | | Connection | Keep it | Nothing to do; it ages out of the 7-day window | | Connection | Reject it | `DELETE /map/relations/:id` (Pro) | | Duplicate | Not a duplicate | `POST /ideas/duplicates/:id/dismiss` | | Duplicate | Merge | `POST /ideas/duplicates/:id/merge` | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Queue returned, or keep recorded | | `400 Bad Request` | `:ideaId` is not a number | | `401 Unauthorized` | Missing or invalid auth | | `404 Not Found` | The user record is missing (the queue endpoint only) | ## Next Steps - [Review concepts](https://docs.neuralconfig.com/neuralrepo/organize/review/): How the Review tab presents this queue and the habits it is designed around. - [Duplicates](https://docs.neuralconfig.com/neuralrepo/api/duplicates/): The full detection payload, and which idea survives each kind of merge. - [User & Settings](https://docs.neuralconfig.com/neuralrepo/api/user/#user-settings): Change stale_threshold_days, or turn stale nudge emails off. - [Relations & Graph](https://docs.neuralconfig.com/neuralrepo/api/relations/): Delete a proposed connection, or add your own. --- # Search Source: https://docs.neuralconfig.com/neuralrepo/api/search/ ## Search Ideas `GET /api/v1/ideas/search` Searches your unarchived ideas by meaning (default) or by keyword. The two modes are separate engines with separate result shapes — the response tells you which one ran. ### Query Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `q` | string | Yes | — | Search query, max 500 characters | | `mode` | string | No | semantic | `keyword` or `semantic`; anything else returns `400` | | `limit` | number | No | `20` | Clamped to `100`. Keyword mode can return up to **2×** this | | `fts_mode` | string | No | `or` | Keyword matching: `or`, `and`, or `exact` | | `min_score` | number | No | `0.2` | Semantic score floor; overrides your `search_threshold` setting | There is no `offset` — search is not paginated. Raise `limit` instead. **There is no automatic keyword fallback:** A semantic search that finds nothing above the score floor returns **an empty `results` array** with `search_type: "semantic"`. It does not silently retry as a keyword search. If you want keyword behaviour, ask for it with `mode=keyword` — that is the only way to reach the full-text index. (The MCP `search_ideas` tool and the agent *do* fall back; this endpoint and the CLI do not.) ### Semantic Search The default mode. The query is embedded, matched against your idea vectors, filtered by score, and then **reranked** by a second model before being cut to `limit`. ```bash curl "https://neuralrepo.com/api/v1/ideas/search?q=user+interface+theming" \ -H "X-API-Key: nrp_YOUR_KEY" ``` Two consequences worth knowing: - Results are ordered by the reranker, not by raw `score`, so scores are **not** guaranteed to descend down the list. - Ideas captured seconds ago may be missing. An idea is only searchable once its queued embedding lands (`vectorize_id` stops being `null`). Only ideas scoring **strictly above** `min_score` survive. The default floor is `0.2`; your `search_threshold` user setting replaces it, and an explicit `min_score` parameter overrides both. ### Keyword Search Keyword mode queries the SQLite FTS5 index over titles and bodies, ordered by BM25 rank. **curl** ```bash curl "https://neuralrepo.com/api/v1/ideas/search?q=dark+mode&mode=keyword" \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const params = new URLSearchParams({ q: "dark mode", mode: "keyword", fts_mode: "and", }); const res = await fetch( `https://neuralrepo.com/api/v1/ideas/search?${params}`, { headers: { "X-API-Key": "nrp_YOUR_KEY" } } ); const results = await res.json(); ``` Before matching, the query is lowercased, stripped of punctuation, and split on whitespace. Tokens of one character and common stop words (`the`, `and`, `for`, `what`, `this`, and about sixty others) are dropped. **A query made entirely of stop words matches nothing** — `"how to do it"` reduces to zero tokens and returns an empty array. `fts_mode` controls how the surviving tokens are combined: | `fts_mode` | Behaviour | `dark mode` becomes | |---|---|---| | `or` (default) | Any token matches | `"dark" OR "mode"` | | `and` | Every token must match | `"dark" "mode"` | | `exact` | The raw phrase, quoted; stop words are kept | `"dark mode"` | **Keyword mode over-delivers:** Keyword mode asks the index for `limit × 2` rows and returns all of them, so `limit=20` can come back with 40 results. Slice client-side if the count matters. ### Response **`200 OK`** The response echoes the `query`, reports `search_type` (`semantic` or `fts`), and — in keyword mode — the `fts_mode` that ran. ```json { "query": "user interface theming", "search_type": "semantic", "results": [ { "id": 137, "number": 42, "title": "Add dark mode support", "body": "Users have requested a dark theme...", "status": "exploring", "source": "web", "source_url": null, "is_archived": 0, "tags": ["ui", "feature-request"], "score": 0.92, "created_at": "2026-03-20 10:00:00", "updated_at": "2026-03-22 14:30:00" } ] } ``` ### Score Field | Mode | `score` | |------|---------| | `fts` | Always `null` — the BM25 rank orders the rows but is not returned | | `semantic` | Cosine similarity between the query and the idea embedding | ## Free-plan semantic allowance Free accounts get **10 semantic searches per calendar month**. Once they are spent, the endpoint serves keyword results instead and says so: ```json { "query": "user interface theming", "search_type": "fts", "fts_mode": "or", "results": [], "semantic_limit_reached": true, "semantic_limit_message": "You've used all 10 semantic searches this month. Showing keyword results instead. Upgrade to Pro for unlimited semantic search." } ``` Three details that surprise people: - The counter is incremented **before** the search runs, so a search that errors or matches nothing still costs one of the ten. - `mode=keyword` requests are free and never touch the counter. - The count resets on the first of the month; the underlying counter is stored per account-month with a 35-day expiry. Pro accounts have no semantic-search limit. Both plans have unlimited keyword search. ## Comparison | | Keyword (`mode=keyword`) | Semantic (default) | |---|---|---| | Engine | SQLite FTS5, BM25 rank | Vector index + reranker | | Matches exact words | Yes | Not necessarily | | Finds conceptual matches | No | Yes | | Needs an embedding | No — searchable immediately | Yes — after the queue processes the idea | | Returns a `score` | No (`null`) | Yes | | Respects `limit` | Returns up to 2× | Yes | | Free-plan limit | None | 10 per month | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Search completed — including when `results` is empty | | `400 Bad Request` | Missing `q`, or a `mode` other than `keyword`/`semantic` | | `401 Unauthorized` | Missing or invalid auth | ## Next Steps - [Search concepts](https://docs.neuralconfig.com/neuralrepo/organize/search/): How embedding, reranking, and thresholds fit together across every NeuralRepo surface. - [User & Settings](https://docs.neuralconfig.com/neuralrepo/api/user/): Set search_threshold and the other similarity thresholds on your profile. - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/): List and filter ideas when you already know the status, tag, or connection you want. - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): The daily request quota that applies on top of the monthly semantic allowance. --- # Tags Source: https://docs.neuralconfig.com/neuralrepo/api/tags/ Tags categorize and filter ideas. Each tag has a name, an optional colour, and — on the list endpoint — a count of ideas using it. Tag names are unique per account, and tags are created implicitly whenever you attach an unknown name to an idea. ## List Tags `GET /api/v1/tags` Returns every tag on the account, most-used first, then alphabetically. There is no pagination. **curl** ```bash curl https://neuralrepo.com/api/v1/tags \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/tags", { headers: { "X-API-Key": "nrp_YOUR_KEY" }, }); const { tags } = await res.json(); ``` **Response `200 OK`** ```json { "tags": [ { "id": 1, "user_id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "name": "feature-request", "color": "#4f46e5", "vectorize_id": "tag_1", "created_at": "2026-02-01 09:00:00", "idea_count": 12 }, { "id": 2, "user_id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "name": "bug", "color": null, "vectorize_id": null, "created_at": "2026-02-03 11:20:00", "idea_count": 5 } ] } ``` `idea_count` counts **unarchived** ideas only, so archiving ideas lowers it and a tag can sit at zero without being deleted. `vectorize_id` is `null` until the tag's embedding is generated in the background — until then it cannot appear in similarity results. ## Find Similar Tags `GET /api/v1/tags/similar` Finds tags whose embeddings are close to a given tag's. Useful for spotting near-duplicate tags (`ui` vs `frontend`) before they fragment your organization. ### Query Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `tag` | string | Yes | Tag **name** (not id) to compare against | | `limit` | number | No | Max results (default `5`, clamped to `20`) | **curl** ```bash curl "https://neuralrepo.com/api/v1/tags/similar?tag=frontend&limit=5" \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "similar": [ { "name": "ui", "idea_count": 8, "similarity": 0.82 }, { "name": "react", "idea_count": 5, "similarity": 0.76 }, { "name": "css", "idea_count": 3, "similarity": 0.71 } ] } ``` | Situation | Response | |---|---| | Tag name not found on your account | `404 { "error": "Tag \"frontend\" not found" }` | | Tag exists but has no embedding yet | `200 { "similar": [], "message": "Tag has no embedding yet" }` | | `tag` parameter omitted | `400 { "error": "Missing \"tag\" query parameter" }` | There is no score floor — every neighbour the index returns is listed, so a small account with unrelated tags still gets results with low `similarity` values. Judge by the number, not by presence in the list. ## Create Tag `POST /api/v1/tags` ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | Yes | 1–50 characters, unique within your account | | `color` | string | No | Hex colour, exactly `#rrggbb` (six digits, `#f00` is rejected) | **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/tags \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "backend", "color": "#059669"}' ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/tags", { method: "POST", headers: { "X-API-Key": "nrp_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ name: "backend", color: "#059669" }), }); const { tag } = await res.json(); ``` **Response `201 Created`** ```json { "tag": { "id": 3, "user_id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "name": "backend", "color": "#059669", "vectorize_id": null, "created_at": "2026-03-24 09:00:00" } } ``` Creating a tag that already exists returns `409 { "error": "Tag name already exists" }`. You rarely need this endpoint — attaching an unknown tag name to an idea creates it for you. ## Update Tag `PATCH /api/v1/tags/:id` Update the name, colour, or both. Only provided fields change. Takes the tag **id**, not its name. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | No | 1–50 characters | | `color` | string | No | Hex colour in `#rrggbb` format | ```bash curl -X PATCH https://neuralrepo.com/api/v1/tags/3 \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"color": "#0ea5e9"}' ``` **Response `200 OK`** ```json { "tag": { "id": 3, "user_id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "name": "backend", "color": "#0ea5e9", "vectorize_id": null, "created_at": "2026-03-24 09:00:00" } } ``` **Renaming onto an existing name fails with a 500:** The uniqueness conflict that `POST` reports as a clean `409` is not caught here: renaming tag `backend` to a name you already use returns `500 { "error": "Internal server error" }` and changes nothing. Check the tag list first, and treat a `500` from this endpoint as a name collision rather than an outage. To combine two tags, retag the ideas and delete the empty one. Renaming a tag does not re-embed it, so `GET /tags/similar` keeps comparing against the old name's embedding until the tag is re-embedded in the background. ## Delete Tag `DELETE /api/v1/tags/:id` Deletes the tag and detaches it from every idea. The ideas themselves are untouched — no bodies, statuses, or other tags change. ```bash curl -X DELETE https://neuralrepo.com/api/v1/tags/3 \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "success": true } ``` Deleting a tag that is not yours or does not exist returns `404`. There is no undo — recreating the tag does not restore its associations. **Unused tags are deleted for you:** Every write that sets an idea's tags — `POST /ideas`, `PATCH /ideas/:id` with `tags`, and the bulk endpoint — finishes by deleting **all** of your tags that are attached to no idea. A tag you created here and have not used yet will disappear the next time any idea's tags are replaced, taking its colour with it. Create tags by attaching them, not ahead of time. ## Tag Schema | Field | Type | Description | |-------|------|-------------| | `id` | number | Unique tag identifier | | `user_id` | string | Owning account | | `name` | string | Display name, unique per account | | `color` | string \| null | Hex colour code | | `vectorize_id` | string \| null | Embedding id; `null` until the tag has been embedded | | `created_at` | string | UTC `YYYY-MM-DD HH:MM:SS` | | `idea_count` | number | Unarchived ideas using this tag — **list endpoint only** | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Successful read, update, or delete | | `201 Created` | Tag created | | `400 Bad Request` | Validation error (name length, colour format, non-numeric id, missing `tag` param) | | `401 Unauthorized` | Missing or invalid auth | | `404 Not Found` | Tag not found, or not yours | | `409 Conflict` | `POST` only — a tag with that name already exists | | `500 Internal Server Error` | `PATCH` only — the new name collides with an existing tag | ## Next Steps - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/#bulk-update-ideas): Attach tags at capture time, replace them wholesale, or add and remove them in bulk. - [Tag concepts](https://docs.neuralconfig.com/neuralrepo/organize/tags/): Auto-tagging, tag hygiene, and when merging two tags is worth the effort. - [Search](https://docs.neuralconfig.com/neuralrepo/api/search/): Filtering by tag versus searching by meaning — and when each one finds more. - [Error Handling](https://docs.neuralconfig.com/neuralrepo/api/errors/): What each status code means across the whole API. --- # User & Settings Source: https://docs.neuralconfig.com/neuralrepo/api/user/ Everything under `/api/v1/user` acts on the authenticated account. Three of these endpoints are Pro-gated; the rest, including every export, work on any plan. ## Get Current User `GET /api/v1/user/me` Returns the profile, the current idea count, and trial state. Encrypted provider keys are stripped and replaced by `has_*_key` booleans. **curl** ```bash curl https://neuralrepo.com/api/v1/user/me \ -H "X-API-Key: nrp_YOUR_KEY" ``` **JavaScript** ```js const res = await fetch("https://neuralrepo.com/api/v1/user/me", { headers: { "X-API-Key": "nrp_YOUR_KEY" }, }); const user = await res.json(); ``` **Response `200 OK`** ```json { "id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "email": "you@example.com", "display_name": "Jane Doe", "avatar_url": "https://avatars.githubusercontent.com/u/1?v=4", "plan": "pro", "settings_json": "{\"preferred_ai_provider\":\"anthropic\"}", "has_anthropic_key": true, "has_openai_key": false, "has_openrouter_key": false, "trial_ends_at": "2026-04-01 00:00:00", "trial_used": 1, "active_idea_count": 137, "is_trialing": true, "trial_days_remaining": 6, "created_at": "2026-01-15 08:00:00", "updated_at": "2026-03-20 12:00:00" } ``` | Field | Meaning | |---|---| | `plan` | `free` or `pro`. **Trialing accounts read `pro`** — the gates treat them as Pro | | `active_idea_count` | Unarchived ideas; compare with 50 to see how close a Free account is to its cap | | `is_trialing` | `true` only when the plan is `pro`, the trial has not ended, and there is no Stripe subscription | | `trial_days_remaining` | Whole days, rounded up; `null` when not trialing | | `email_capture_bare_optin` | `1` if you opted in to the shared `idea@` address for the transition, else `0`. Your personal capture address is **not** in this response; see [Email Capture Address](#email-capture-address) | **Note:** `plan` alone cannot tell you whether someone is paying. A trial and a subscription both read `pro`; `is_trialing` is what distinguishes them. ## Update Profile `PATCH /api/v1/user/me` ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `display_name` | string | No | 1–100 characters | | `settings_json` | string | No | A JSON **string**, max 10,000 characters. See [User Settings](#user-settings) | ```bash curl -X PATCH https://neuralrepo.com/api/v1/user/me \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"display_name": "Jane D.", "settings_json": "{\"search_threshold\":0.3}"}' ``` **settings_json replaces the whole blob:** It is stored as an opaque string, so a partial write drops every setting you left out. Read `settings_json` from `GET /user/me`, parse it, change one key, and send the whole object back. Unknown or out-of-range keys are ignored when the settings are read, not rejected here. **Response `200 OK`** — the updated user object, with the same stripped fields as `GET /user/me` but without `active_idea_count` and the trial fields. ## API Keys ### List Keys `GET /api/v1/user/api-keys` Returns every key on the account, newest first, with **no key material** — not even a masked prefix. **Response `200 OK`** ```json { "api_keys": [ { "id": "9c4f1b7e2d3a48f0b6e5c8d1a2f7b904", "label": "CI Pipeline", "scopes": null, "source": "manual", "last_used_at": "2026-03-24 08:12:00", "created_at": "2026-03-20 10:00:00" } ] } ``` `source` is `manual` for keys made here and `mcp` for tokens minted by the MCP OAuth flow; `scopes` is `null` on every manual key, which means full access. ### Create Key `POST /api/v1/user/api-keys` **Pro plan required:** Returns `403 { "error": "API access requires a Pro plan", "pro_required": true, "feature": "API access", "upgrade_url": "..." }` on Free, and no key is created. | Field | Type | Required | Description | |-------|------|----------|-------------| | `label` | string | No | Max 100 characters; defaults to `default` | **Response `201 Created`** ```json { "id": "9c4f1b7e2d3a48f0b6e5c8d1a2f7b904", "key": "nrp_a1b2c3d4...", "label": "CI Pipeline", "created_at": "2026-03-24 09:00:00" } ``` :::caution The full `key` value is only returned at creation time — only its hash is stored. Store it securely; a lost key can be revoked but never recovered. ::: ### Delete Key `DELETE /api/v1/user/api-keys/:id` Revokes the key immediately, using the `id` from the list endpoint (a 32-character hex string, not the key itself). Works on every plan. Returns `404` if the id is not yours. **Response `200 OK`** ```json { "success": true } ``` ## MCP Tokens `GET /api/v1/user/mcp-tokens` Lists the tokens issued by the MCP OAuth flow when you authorize a client such as Claude. It **does not create** tokens — the client does that during authorization. **Response `200 OK`** ```json { "tokens": [ { "id": "b17d0e59a4c34f8e91d2c6b7f0a35e82", "label": "MCP (claude-ai)", "scopes": "ideas:read ideas:write", "source": "mcp", "last_used_at": "2026-03-24 08:00:00", "created_at": "2026-03-20 10:00:00" } ] } ``` The client's identity appears inside `label`; there is no separate `client_id` field. Revoke a token with `DELETE /api/v1/user/api-keys/:id` — MCP tokens live in the same store as API keys. Revoking one also revokes that client's refresh tokens, so the client has to authorize again. An MCP token stops working **30 days** after it was issued. The client uses its refresh token to get a new one, and the refresh deletes the old token, so the list normally holds one token per connected client. The daily retention sweep removes tokens older than 30 days. ## BYOK (Bring Your Own Key) AI features that run on your own provider account — `POST /ideas/:id/develop` in this API — need a stored provider key. NeuralRepo encrypts it at rest and never returns it. ### Save Provider Key `PUT /api/v1/user/byok/:provider` Pro only. `:provider` must be `anthropic`, `openai`, or `openrouter`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `api_key` | string | Yes | Your provider key; must be at least 10 characters | ```bash curl -X PUT https://neuralrepo.com/api/v1/user/byok/anthropic \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"api_key": "sk-ant-..."}' ``` The key is stored without being tested — a typo is accepted here and fails later at `develop` time. Run the test endpoint below to confirm it works. **Response `200 OK`** ```json { "success": true, "provider": "anthropic" } ``` ### Delete Provider Key `DELETE /api/v1/user/byok/:provider` Removes the stored key. Unlike saving and testing, this is **not** Pro-gated — an account that lapses to Free can still delete keys it stored while on Pro. **Response `200 OK`** ```json { "success": true, "provider": "anthropic" } ``` ### Test Provider Key `POST /api/v1/user/byok/:provider/test` Pro only. Decrypts the stored key and makes a minimal live call to the provider. ```bash curl -X POST https://neuralrepo.com/api/v1/user/byok/anthropic/test \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "ok": true } ``` A failure is also `200`, with the reason in the body: `{ "ok": false, "error": "No anthropic key configured" }` when nothing is stored, or the provider's own message when the call is rejected. **Check `ok`, not the status code.** ### Check All Providers `GET /api/v1/user/byok/status` Returns which providers have keys and which one will actually be used. **Response `200 OK`** ```json { "active_provider": "anthropic", "has_anthropic_key": true, "has_openai_key": false, "has_openrouter_key": false } ``` `active_provider` resolves as: your `preferred_ai_provider` setting **if a key for it is stored**, otherwise the first available in the fixed order Anthropic → OpenAI → OpenRouter. It is `null` when no keys are stored — which is when `develop` returns `needs_byok`. Setting a preference for a provider you have no key for is silently ignored rather than erroring. ## Shortcuts `GET /api/v1/user/shortcuts` Returns the iCloud Shortcut URL used for Siri capture. The same URL for every account. ```bash curl https://neuralrepo.com/api/v1/user/shortcuts \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "quick_capture_url": "https://www.icloud.com/shortcuts/..." } ``` ## Support Link `GET /api/v1/user/support-link` Mints a single-use, pre-authenticated URL into the NeuralRepo support site for the account's email address, so you arrive already signed in. **Response `200 OK`** ```json { "url": "https://support.neuralrepo.com/..." } ``` Returns `502 { "error": "Failed to generate support link" }` if the support service is unreachable. Treat the URL as a credential — anyone holding it is signed in as you. ## Test Digest `POST /api/v1/user/test-digest` Sends the weekly digest email to your own address immediately, and returns the rendered content so you can preview it without waiting for Sunday. ```bash curl -X POST https://neuralrepo.com/api/v1/user/test-digest \ -H "X-API-Key: nrp_YOUR_KEY" ``` **Response `200 OK`** ```json { "success": true, "content": "..." } ``` If sending fails, the endpoint returns `500` with the underlying error message. ## User Settings `settings_json` is a JSON string on the profile. Values outside the stated ranges are discarded when the settings are parsed, and the default applies instead — nothing reports the mistake. | Field | Type | Range | Default | Description | |-------|------|-------|---------|-------------| | `preferred_ai_provider` | string | `anthropic`, `openai`, `openrouter` | — | Provider for `develop`, if a key for it is stored | | `search_threshold` | number | 0.1–0.9 | `0.2` | Semantic results must score **above** this | | `dedup_threshold` | number | 0.1–0.9 | `0.75` | Score above which a pair is flagged as a duplicate | | `related_threshold` | number | 0.1–0.9 | `0.55` | Score above which a `related` edge is created automatically | | `code_mode` | boolean | — | — | Exposes the MCP code-mode tool surface | | `weekly_digest` | boolean | — | opted in | Only an explicit `false` stops the Sunday digest | | `stale_nudges` | boolean | — | opted in | Only an explicit `false` stops stale-idea nudge emails | | `stale_threshold_days` | integer | 7–180 | `30` | Days before a `captured` idea counts as stale | **Note:** The three similarity thresholds cannot be set to 0 or 1. A value like `0` or `0.95` is dropped and the default silently applies, so a search that "ignores your threshold" usually means the value was out of range. ## Data Export Export is available on **every plan**, and all three endpoints are `POST` with no body. Each covers up to 10,000 ideas and **excludes archived ideas** — export before you archive, not after. ### JSON Export `POST /api/v1/user/export` The complete dump: profile, ideas with their tags, links, and relations, plus the tag list with counts. Returned as a JSON body (not a file download), stamped with `exported_at`. ```bash curl -X POST https://neuralrepo.com/api/v1/user/export \ -H "X-API-Key: nrp_YOUR_KEY" \ -o export.json ``` ### CSV Export `POST /api/v1/user/export/csv` A flat table for spreadsheets: `id`, `number`, `title`, `body`, `status`, `tags` (semicolon separated), `source`, `created_at`. Sent as a UTF-8 BOM'd `text/csv` attachment named `neuralrepo-ideas-YYYY-MM-DD.csv`. Relations and links are not included. ```bash curl -X POST https://neuralrepo.com/api/v1/user/export/csv \ -H "X-API-Key: nrp_YOUR_KEY" \ -o export.csv ``` ### Markdown Export `POST /api/v1/user/export/markdown` A ZIP of one Markdown file per idea, named `NNNN-slug.md` from the idea's display number and title. Each file carries YAML frontmatter (`number`, `title`, `status`, `source`, `tags`, `created`, `updated`), the body, a Links section, and a Related section where **relations become `[[wiki-links]]`** — so an Obsidian vault reproduces your mind map graph. ```bash curl -X POST https://neuralrepo.com/api/v1/user/export/markdown \ -H "X-API-Key: nrp_YOUR_KEY" \ -o neuralrepo-markdown.zip ``` ## Email Capture Address The personal `idea+@neuralrepo.com` address that [email capture](https://docs.neuralconfig.com/neuralrepo/capture/email/) delivers to. These routes work on every plan, but mail is captured only on Pro. | Endpoint | Does | |---|---| | `GET /api/v1/user/email-capture` | Returns your address, creating it on the first call | | `POST /api/v1/user/email-capture/rotate` | Replaces the address. The old one is rejected from then on | | `PUT /api/v1/user/email-capture/bare-address` | Body `{ "opt_in": true }` or `{ "opt_in": false }`: opts in to, or out of, the shared `idea@` address until the cut-off | All three return the same shape: ```json { "address": "idea+3f9c0a1b2c3d4e5f6a7b8c9d@neuralrepo.com", "bare_address": "idea@neuralrepo.com", "bare_address_opt_in": false, "bare_address_open": true, "bare_address_sunset": "2026-10-24T00:00:00Z" } ``` | Field | Meaning | |---|---| | `address` | Your personal capture address. Treat it as a secret: anyone who has it can add ideas to your repo | | `bare_address_opt_in` | Whether mail from your account email to `idea@` is still captured | | `bare_address_open` | `false` once `bare_address_sunset` has passed; after that `idea@` rejects everything | | `bare_address_sunset` | When the shared address stops capturing | `PUT /bare-address` returns `400` `opt_in (boolean) is required` for a missing or non-boolean `opt_in`, and `409` `The shared address has been retired; use your personal capture address` for `opt_in: true` after the cut-off. Opting **out** always works. ## Delete Account `DELETE /api/v1/user/me` Deletes the account and everything it owns. [Delete Data](https://docs.neuralconfig.com/neuralrepo/organize/delete-data/) lists what is removed, what is kept, and what is out of reach. **Web session only:** an API key or MCP token gets `403`. **Request body:** ```json { "confirm_email": "you@example.com" } ``` `confirm_email` must match the account's email, ignoring letter case and surrounding spaces. **Response `200 OK`:** `{ "success": true, ... }`, with counts of what was deleted (`rows`, `vectors_deleted`, `kv_keys_deleted`), whether a subscription was canceled (`subscription_canceled`), and the `out_of_reach` list. The session cookie is cleared. | Status | Body `error` | |---|---| | `400` | `confirm_email is required`, or `The email you typed does not match your account` | | `403` | `Sign in on the web to delete your account`, or `The admin account cannot be deleted here` | | `404` | `User not found` | | `502` | `Account deletion did not finish. You are still signed in; try again to complete it.` Nothing past the failed step was deleted, and a retry continues | ## Status Codes | Status | Meaning | |--------|---------| | `200 OK` | Successful read, update, delete, account deletion, or export | | `201 Created` | API key created | | `400 Bad Request` | Validation error, or an unknown BYOK provider | | `401 Unauthorized` | Missing or invalid auth | | `403 Forbidden` | Pro-only endpoint on a Free plan (`pro_required: true`), or account deletion without a web session or on the admin account | | `404 Not Found` | API key id not yours, or the user record is missing | | `409 Conflict` | Opting in to the shared `idea@` address after its cut-off | | `500 Internal Server Error` | `test-digest` failed to send | | `502 Bad Gateway` | The support service could not mint a link, or account deletion did not finish | ## Next Steps - [Authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): How keys, sessions, and MCP tokens are resolved, and what scopes actually apply. - [BYOK setup](https://docs.neuralconfig.com/neuralrepo/integrations/byok/): Which models each provider uses, and what NeuralRepo sends them. - [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/): The develop endpoint that consumes your BYOK key, and the Free-plan idea cap. - [Weekly digest](https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/): What the Sunday email contains and how stale_threshold_days changes it. --- # Data Model Source: https://docs.neuralconfig.com/neuralrepo/architecture/data-model/ NeuralRepo uses **Cloudflare D1** (SQLite) as its primary database. The schema is designed for single-user data isolation, efficient full-text search, and flexible idea relations. **Note:** **Every `created_at` and `updated_at` in D1 is `datetime('now')`** — the SQLite format `YYYY-MM-DD HH:MM:SS`, in UTC, with no `T`, no `Z`, and no offset. It is not ISO-8601, and parsing it as such fails in some runtimes. See [Timestamps](https://docs.neuralconfig.com/neuralrepo/api/overview/#timestamps). ## Entity Relationship Diagram ```mermaid erDiagram users ||--o{ auth_accounts : "has" users ||--o{ sessions : "has" users ||--o{ api_keys : "has" users ||--o{ ideas : "owns" users ||--o{ billing_events : "has" ideas ||--o{ idea_tags : "has" ideas ||--o{ idea_links : "has" ideas ||--o{ idea_relations : "source" ideas ||--o{ idea_relations : "target" ideas ||--o{ duplicate_detections : "source" ideas ||--o{ duplicate_detections : "target" ideas ||--o{ attachments : "has" ideas ||--o| ideas : "parent" tags ||--o{ idea_tags : "has" users ||--o{ oauth_authorization_codes : "has" users ||--o{ oauth_refresh_tokens : "has" ``` `magic_links` and `schema_migrations` stand outside this graph: magic links are keyed by email address rather than by user (the account may not exist yet), and migrations track the schema itself. ## Core Tables ### users The central user record. All data queries are scoped by `user_id`. | Column | Type | Description | |--------|------|-------------| | `id` | TEXT | Primary key — 32 lowercase hex characters from `randomblob(16)`, not a dashed UUID | | `email` | TEXT | Unique email address | | `display_name` | TEXT | Display name | | `avatar_url` | TEXT | Profile avatar URL | | `plan` | TEXT | `free` or `pro` | | `stripe_customer_id` | TEXT | Stripe customer reference | | `stripe_subscription_id` | TEXT | Stripe subscription reference | | `anthropic_api_key_encrypted` | TEXT | Encrypted Anthropic API key (BYOK) | | `openai_api_key_encrypted` | TEXT | Encrypted OpenAI API key (BYOK) | | `openrouter_api_key_encrypted` | TEXT | Encrypted OpenRouter API key (BYOK) | | `settings_json` | TEXT | User settings (JSON string, default `{}`) | | `trial_ends_at` | TEXT | When the Pro trial lapses; the daily cron downgrades past this | | `trial_used` | INTEGER | `1` once a trial has been taken, so it cannot be taken twice | | `latest_accepted_terms_version` | TEXT | Terms version last accepted; the evidence is in `terms_acceptances` | | `latest_accepted_privacy_version` | TEXT | Privacy Policy version last accepted | | `latest_accepted_addenda_version` | TEXT | Addenda version last accepted | | `latest_accepted_at` | TEXT | When those versions were accepted | | `email_capture_token` | TEXT | Unique secret in the `idea+@neuralrepo.com` capture address, created on first view | | `email_capture_bare_optin` | INTEGER | `1` if opted in to capture from the bare `idea@` address, until the 2026-10-24 sunset | | `created_at` | TEXT | `YYYY-MM-DD HH:MM:SS` UTC | | `updated_at` | TEXT | `YYYY-MM-DD HH:MM:SS` UTC | ### auth_accounts Links external sign-in providers to a user. | Column | Type | Description | |--------|------|-------------| | `id` | TEXT | Primary key | | `user_id` | TEXT | FK to users | | `provider` | TEXT | `github`, `google`, `apple`, or `email` | | `provider_account_id` | TEXT | External account ID; unique together with `provider` | | `provider_email` | TEXT | Email from the provider | | `created_at` | TEXT | Creation timestamp | ### sessions Active user sessions. Tokens are SHA-256 hashed before storage. | Column | Type | Description | |--------|------|-------------| | `id` | TEXT | Primary key | | `user_id` | TEXT | FK to users | | `token_hash` | TEXT | SHA-256 hash of the session token (unique) | | `expires_at` | TEXT | Rolling 30-day expiry, pushed forward on every authenticated request | | `created_at` | TEXT | Creation timestamp | ### api_keys API keys for programmatic access. Keys are hashed with SHA-256. | Column | Type | Description | |--------|------|-------------| | `id` | TEXT | Primary key | | `user_id` | TEXT | FK to users | | `key_hash` | TEXT | SHA-256 hash of the `nrp_` prefixed key (unique) | | `label` | TEXT | User-assigned label, default `default` | | `scopes` | TEXT | Space-separated scope list. `NULL` — full access — on every key you create; scopes only appear on MCP OAuth tokens. | | `source` | TEXT | Key source, default `manual` | | `last_used_at` | TEXT | Timestamp of most recent use | | `created_at` | TEXT | Creation timestamp | ### magic_links One-time email sign-in tokens, keyed by email rather than user. | Column | Type | Description | |--------|------|-------------| | `id` | TEXT | Primary key | | `email` | TEXT | Recipient address | | `token_hash` | TEXT | SHA-256 hash of the emailed token (unique) | | `expires_at` | TEXT | 15 minutes after issue | | `used_at` | TEXT | Set on redemption — the row is marked, not deleted | | `created_at` | TEXT | Creation timestamp | ### ideas The core content table. | Column | Type | Description | |--------|------|-------------| | `id` | INTEGER | Auto-increment primary key — **globally unique**, and the identifier every API, CLI, and MCP call takes | | `user_id` | TEXT | FK to users | | `number` | INTEGER | **Per-user display number**, unique with `user_id`. This is the `#42` the UI and CLI print. Nothing resolves a number back to an idea. | | `title` | TEXT | Idea title (max 200 chars) | | `body` | TEXT | Markdown body (max 50,000 chars) | | `status` | TEXT | `captured`, `exploring`, `building`, `shipped`, `shelved` | | `source` | TEXT | Origin: `web`, `cli`, `claude-mcp`, `siri`, `email`, `api`, `shortcut`, `ios` | | `source_url` | TEXT | Optional URL reference | | `source_summary` | TEXT | Summary from source | | `parent_id` | INTEGER | Self-referencing FK; `ON DELETE SET NULL` | | `vectorize_id` | TEXT | Cloudflare Vectorize embedding ID (`idea_`). Null until the queue has processed the idea. | | `is_archived` | INTEGER | Soft delete flag (0 = active, 1 = archived) | | `created_at` | TEXT | Creation timestamp | | `updated_at` | TEXT | Last update timestamp | **Danger:** **`id` and `number` are different numbers and only coincide for the first account ever created.** `number` is `MAX(number) + 1` within one user; `id` is the global autoincrement. Every listing prints `number`; every lookup takes `id`. See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). ### tags Unique tag definitions scoped by user. | Column | Type | Description | |--------|------|-------------| | `id` | INTEGER | Primary key | | `user_id` | TEXT | FK to users; unique together with `name` | | `name` | TEXT | Tag name (lowercase, unique per user) | | `color` | TEXT | Optional hex color | | `vectorize_id` | TEXT | Vector ID for the tag's own embedding (`tag--`) | | `created_at` | TEXT | Creation timestamp | ### idea_tags Many-to-many join between ideas and tags. The composite primary key is `(idea_id, tag_id)`, so the same tag cannot be attached twice. | Column | Type | Description | |--------|------|-------------| | `idea_id` | INTEGER | FK to ideas, cascade delete | | `tag_id` | INTEGER | FK to tags, cascade delete | ### idea_links URLs attached to ideas. | Column | Type | Description | |--------|------|-------------| | `id` | INTEGER | Primary key | | `idea_id` | INTEGER | FK to ideas | | `url` | TEXT | Link URL | | `title` | TEXT | Optional display title | | `link_type` | TEXT | `url`, `claude-chat`, `github-repo`, `github-issue`, `attachment` | | `created_at` | TEXT | Creation timestamp | ### idea_relations Typed directional links between ideas — the mind map's edges. | Column | Type | Description | |--------|------|-------------| | `id` | INTEGER | Primary key | | `user_id` | TEXT | FK to users | | `source_idea_id` | INTEGER | FK to ideas | | `target_idea_id` | INTEGER | FK to ideas | | `relation_type` | TEXT | `related`, `parent`, `blocks`, `inspires`, `supersedes` — plus `duplicate`, which the constraint permits but nothing writes | | `score` | REAL | Similarity score (null for manual relations) | | `note` | TEXT | Optional description | | `created_by` | TEXT | `user` or `system` | | `created_at` | TEXT | Creation timestamp | Uniqueness is on `(source_idea_id, target_idea_id, created_by)`. That third column is load-bearing: it lets a manual link and an auto-detected one coexist between the same pair, and it is why a second *manual* link between two ideas fails while the system can still add its own. ### duplicate_detections Records when the system detects similar ideas. | Column | Type | Description | |--------|------|-------------| | `id` | INTEGER | Primary key | | `user_id` | TEXT | FK to users | | `idea_id` | INTEGER | The newer/suspected duplicate | | `duplicate_of_id` | INTEGER | The existing original idea | | `similarity_score` | REAL | Cosine similarity (0.0 to 1.0) | | `status` | TEXT | `pending`, `merged`, `dismissed` | | `created_at` | TEXT | Creation timestamp | A unique index on `(idea_id, duplicate_of_id)` means re-running detection cannot create the same detection twice. ### attachments File metadata, with the object itself in R2 under `r2_key`. | Column | Type | Description | |--------|------|-------------| | `id` | TEXT | Primary key | | `idea_id` | INTEGER | FK to ideas | | `user_id` | TEXT | FK to users | | `filename` | TEXT | Original filename | | `content_type` | TEXT | MIME type | | `size_bytes` | INTEGER | File size | | `r2_key` | TEXT | Object key in the R2 bucket | | `created_at` | TEXT | Creation timestamp | **Caution:** **Nothing writes to this table.** There is no upload endpoint anywhere in the product, and the matching `attachment` link type has no producer either. The table and the R2 binding exist ahead of the feature — treat file attachments as unimplemented rather than undocumented. ### billing_events An append-only log of Stripe webhook events: `stripe_event_id` (unique, which is what makes webhook delivery idempotent), `event_type`, `amount_cents`, `currency`, the Stripe customer, subscription, and invoice ids, and a `metadata_json` blob. ### OAuth Tables `oauth_authorization_codes` and `oauth_refresh_tokens` support the MCP OAuth flow. Both store SHA-256 hashes rather than the credential. Authorization codes are short-lived (10 minutes) and single-use via `used_at`; refresh tokens expire after 90 days and carry a `revoked_at` column. Both default their `scopes` column to `ideas:read ideas:write`. ### schema_migrations One row per applied migration file (`version`, `applied_at`), so migrations are not re-run. ## Full-Text Search The `ideas_fts` table is an **FTS5 virtual table** that mirrors the `title` and `body` columns of the `ideas` table: ```sql CREATE VIRTUAL TABLE ideas_fts USING fts5( title, body, content='ideas', content_rowid='id' ); ``` Three triggers keep it in sync — `ideas_ai` after insert, `ideas_ad` after delete, and `ideas_au` after update (a delete row followed by a fresh insert). **Note:** Archiving is an `UPDATE`, not a `DELETE`, so an archived idea **stays in the FTS index** — the archived-out filter happens in the query, not in the index. Its *vector* is deleted, though, which is why archived ideas vanish from semantic results immediately and from keyword results only because the query says so. ## Indexes Key indexes for query performance: | Table | Index | Columns | |-------|-------|---------| | ideas | `idx_ideas_user` | `user_id` | | ideas | `idx_ideas_user_status` | `user_id`, `status` | | ideas | `idx_ideas_user_created` | `user_id`, `created_at DESC` | | tags | `idx_tags_user` | `user_id` | | idea_tags | `idx_idea_tags_tag` | `tag_id` | | idea_relations | `idx_relations_source` | `source_idea_id` | | idea_relations | `idx_relations_target` | `target_idea_id` | | idea_links | `idx_idea_links_idea` | `idea_id` | | duplicate_detections | `idx_dupes_user` | `user_id`, `status` | | duplicate_detections | `idx_dupes_pair` (unique) | `idea_id`, `duplicate_of_id` | | sessions | `idx_sessions_token` | `token_hash` | | api_keys | `idx_api_keys_hash` | `key_hash` | | auth_accounts | `idx_auth_accounts_user` | `user_id` | | attachments | `idx_attachments_idea` | `idea_id` | ## Soft Delete Pattern Ideas use a **soft delete** pattern. The `is_archived` column is `0` for active ideas and `1` for archived ideas, and every listing query filters `is_archived = 0`. This preserves relation integrity: an archived idea's rows in `idea_relations` and `idea_tags` survive rather than cascading away. **Caution:** **Archiving drops the vector.** `is_archived` is absent from the update schema; only `POST /ideas/:id/restore` (and MCP `restore_idea`) clears it, and restoring queues the idea to be embedded again. Archived ideas are excluded from every export. **Hard delete is a separate, explicit step.** Only archived ideas can be removed from the database, from a web session: `DELETE /ideas/:id/permanent` or `DELETE /ideas/archived`. Deleting the account removes the user's rows from every table except four. The `users` row is tombstoned rather than deleted, because `terms_acceptances` cascades on user delete. `terms_acceptances` (the consent record) and `billing_events` (the payment record) are kept. `ai_usage` rows are detached (`user_id` set to `NULL`) and age out with the 90-day sweep. See [Delete Data](https://docs.neuralconfig.com/neuralrepo/organize/delete-data/). ## Next Steps - [Tech Stack](https://docs.neuralconfig.com/neuralrepo/architecture/tech-stack/): The Cloudflare services this schema sits on. - [Semantic Search](https://docs.neuralconfig.com/neuralrepo/architecture/semantic-search/): How rows here become vectors, and what the metadata carries. - [API Overview](https://docs.neuralconfig.com/neuralrepo/api/overview/): Idea identifiers, timestamps, and the field limits enforced above. - [Security & Auth](https://docs.neuralconfig.com/neuralrepo/architecture/security/): What the sessions, keys, and OAuth tables protect. --- # Queue Processing Source: https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/ NeuralRepo processes ideas asynchronously using **Cloudflare Queues**. When you create or update an idea, the API returns immediately while background processing handles embedding, duplicate detection, auto-tagging, and vector indexing. ## Architecture Overview The API request returns immediately (`processing: true`) while, on the same request, a queue producer publishes a message to the Cloudflare Queue. A queue consumer — a Workers handler — receives messages in batches of up to 10 and routes each by its message type to one of five pipelines. ```mermaid flowchart TD api["API request
(create / update idea)"] --> resp["Response returned immediately
(processing: true)"] api --> producer["Queue producer
publishes to Cloudflare Queue"] producer --> consumer["Queue consumer (Workers handler)
batch of up to 10 · routes by message type"] consumer --> created["idea_created"] consumer --> updated["idea_updated"] consumer --> meta["idea_metadata_updated"] consumer --> backfill["backfill_vectors"] consumer --> tagfill["backfill_tag_vectors"] ``` **Note:** `processing: true` is a constant on the create response, not a stored column. Nothing ever writes `processing: false` — there is no such field on the idea. The signal that the pipeline finished is the idea's `vectorize_id` becoming non-null. ### Consumer configuration | Setting | Value | |---|---| | Queue | `neuralrepo-processing` | | Max batch size | 10 messages | | Max batch timeout | 30 seconds | | Max retries | 3 | | Dead-letter queue | `neuralrepo-processing-dlq` | ## Message Types Every message has the same envelope: a `type` and a `payload` object. ### idea_created Triggered when a new idea is saved from any surface. This is the most comprehensive pipeline. **Payload:** ```json { "type": "idea_created", "payload": { "idea_id": 138, "user_id": "9f2c7a1b4e8d40c3ab55e1f0d7c92e64", "title": "Browser extension for idea capture", "body": "Highlight text on any page and save it..." } } ``` **Processing steps, in order:** 1. **Fetch the idea** from D1 and load the user's threshold settings. 2. **Create the parent relation**, if the idea was saved with a `parent_id` (a branch or a sub-idea). This is why a branch's relation appears seconds after the branch itself. 3. **Normalize text** — join title and body with a space, trim, collapse whitespace, truncate to 8,192 characters. 4. **Generate the embedding** via Workers AI (`@cf/baai/bge-m3`), with a 30-second timeout. 5. **Duplicate detection** — query Vectorize for the 5 nearest neighbours, excluding `shelved` ideas. Anything above the dedup threshold becomes a `duplicate_detections` row; anything above the related threshold becomes a system-created `related` relation. 6. **Auto-tag**, but only if the idea arrived with **no** tags at all. 7. **Upsert the vector** into Vectorize as `idea_`, with metadata for `user_id`, `idea_id`, `status`, `tags`, `source`, and `created_at`. 8. **Store `vectorize_id`** on the idea row. 9. **Ensure tag embeddings** exist for the idea's tags, creating any that are missing. **Caution:** Steps 6 and 7 are in that order deliberately: auto-tags have to be applied **before** the vector is upserted, or the vector's `tags` metadata would be written empty and tag-filtered searches would miss the idea until something else touched it. **Auto-tagging is not a BYOK feature and it does not ask permission.** Both plans auto-tag, by different means, and the tags are applied directly to the idea rather than stored as suggestions: | Plan | How tags are chosen | |---|---| | Pro | An LLM classifier sees the title, body, your 20 most-used tags, and tags ranked by frequency across the idea's nearest neighbours. | | Free | No AI call. Tags shared by **at least two** of the five nearest neighbours are borrowed, up to three — and only when the idea has at least two related neighbours to borrow from. | ### idea_updated Triggered when an idea's title or body changes. **Payload:** ```json { "type": "idea_updated", "payload": { "idea_id": 138, "user_id": "9f2c7a1b4e8d40c3ab55e1f0d7c92e64", "title": "Browser extension for idea capture", "body": "Updated body text..." } } ``` **Processing steps:** 1. **Reconcile the parent relation** — delete existing parent relations for the idea, then re-create one if `parent_id` is still set. 2. **Re-generate the embedding** from the new text. 3. **Delete system-created relations** for the idea. Manual relations are left alone. 4. **Delete pending duplicate detections** for the idea. 5. **Re-run duplicate detection**, producing fresh detections and `related` relations. 6. **Auto-tag**, if the idea now has no tags. 7. **Re-upsert** the vector under the same ID and **refresh the tag embeddings**. **Caution:** Steps 3 and 4 mean an edit can make auto-detected relations and pending duplicate flags **disappear** — they are re-derived from the new text, not merged with the old set. Manual links survive any number of edits. ### idea_metadata_updated Triggered when only status or tags change — no content change. **Payload:** ```json { "type": "idea_metadata_updated", "payload": { "idea_id": 138, "user_id": "9f2c7a1b4e8d40c3ab55e1f0d7c92e64" } } ``` **Processing steps:** 1. **Fetch the existing vector** by ID and reuse its values. If no vector exists yet, the message is dropped — an idea whose embedding has not landed cannot have its metadata refreshed. 2. **Re-upsert** with the same embedding and fresh metadata, so filtered searches (for example, "search within `building` status") reflect the change. ### backfill_vectors Full re-indexing of one user's ideas. **Payload:** ```json { "type": "backfill_vectors", "payload": { "user_id": "9f2c7a1b4e8d40c3ab55e1f0d7c92e64" } } ``` The handler does not embed anything itself. It selects every unarchived idea for the user and **re-enqueues each one as an `idea_created` message**, so each goes through the full pipeline — including duplicate detection and auto-tagging. **Caution:** Backfill is heavy and it fans out: one message becomes one message per idea. Because each replays `idea_created`, it also re-runs duplicate detection across the account. It is not triggered by normal usage. ### backfill_tag_vectors Re-embeds a user's **tag** vectors — the per-tag embeddings used for tag-similarity features — in batches of 20, each batch queueing the next until every tag is done. ```json { "type": "backfill_tag_vectors", "payload": { "user_id": "9f2c7a1b4e8d40c3ab55e1f0d7c92e64", "offset": 20 } } ``` Unlike the per-idea path, this one **forces** re-embedding of tags that already have a vector. ## Error Handling The consumer wraps each message in a try/catch and treats every failure the same way: | Outcome | Behavior | |----------|----------| | Handler succeeds | `message.ack()` — the message is done | | Handler throws (Workers AI, Vectorize, D1, anything) | `message.retry()` — Cloudflare redelivers it | | Retried more than 3 times | Cloudflare moves it to `neuralrepo-processing-dlq` | | Unknown message type | Logged as an error and **acked** — the message is discarded, not retried | | Idea no longer exists | The handler returns early and acks — archiving an idea mid-flight is not an error | There is no per-error-class strategy and no application-level backoff; retry timing is Cloudflare's. Failures are logged with the error message, and queue-send failures on the producer side are logged and swallowed rather than failing the API request — which is exactly what the hourly backfill cron below exists to repair. ## Cron Triggers Four **Cloudflare Workers Cron Triggers** are live in production. The daily `0 4 * * *` trigger runs two jobs: | Schedule | Job | Scope | |---|---|---| | `0 18 * * SUN` | Weekly digest | Pro users who have not opted out | | `0 9 * * 1` | Stale-idea nudges | All plans, once per user per ~25 days | | `0 4 * * *` | Trial expiry | Users whose trial has ended with no Stripe subscription | | `0 4 * * *` | Data-retention sweep | Deletes expired sessions, spent OAuth codes, expired magic links, MCP access keys older than 30 days, dead refresh tokens, and `ai_usage` rows older than 90 days — see [Delete Data](https://docs.neuralconfig.com/neuralrepo/organize/delete-data/#what-is-cleaned-up-automatically) | | `0 * * * *` | Backfill missed ideas | Up to 50 ideas per run | **Caution:** `docs/CRONS.md` in the source repo lists the stale-idea check as disabled with a "placeholder handler". That is out of date: the trigger is in `wrangler.toml`, the handler is fully implemented, and the emails send. ### Weekly digest 1. Queries Pro users who haven't opted out of the digest in **Settings ▸ Notifications**. 2. For each user, gathers the week's new ideas, pending duplicates, stale ideas, and totals. 3. If the user has a BYOK key, calls the AI provider for a narrative summary; otherwise formats the stats directly. If the AI call fails, it falls back to the stats summary rather than skipping the email. 4. Sends the digest — skipped entirely when there is nothing to report. ### Stale-idea nudges Runs independently of the digest and covers **all plans**. It checks every user who has not opted out, skips anyone with an active KV cooldown key, and emails a list of ideas sitting in `captured` past the user's stale threshold (default 30 days, configurable 7–180). The cooldown is 25 days, so the weekly cron delivers at most one nudge a month per user. The same stale definition feeds the digest's stale section. ### Backfill missed ideas The self-healing half of the queue. Every hour it selects up to 50 unarchived ideas that have `vectorize_id IS NULL` and were created more than five minutes ago — the signature of a queue send that failed — and re-enqueues each as `idea_created`. The five-minute floor keeps it from racing ideas that are simply still in flight. ## Monitoring Queue health can be monitored through the Cloudflare dashboard: - **Messages in queue** — should be near zero during normal operation. - **Messages processed per minute** — spikes after bulk imports and backfills. - **Dead-letter count** — should be zero. Anything in `neuralrepo-processing-dlq` survived three retries and indicates a processing bug. - **Consumer latency** — time from message publish to processing completion. ## Next Steps - [Semantic Search](https://docs.neuralconfig.com/neuralrepo/architecture/semantic-search/): The embedding model, vector storage, and how search actually resolves. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): The user-facing side of the dedup step, including thresholds. - [Weekly Digest](https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/): What the two email crons send, and how to opt out. - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): Auto-tagging from the user's side, on both plans. --- # Security & Auth Source: https://docs.neuralconfig.com/neuralrepo/architecture/security/ NeuralRepo is designed with security at every layer. This page covers authentication, authorization, encryption, and data isolation. ## OAuth Authentication NeuralRepo supports **four** sign-in methods. The three federated ones are implemented with the **Arctic** OAuth library: | Provider | Protocol | Implementation | |----------|----------|----------------| | GitHub | OAuth 2.0 | Arctic | | Google | OpenID Connect | Arctic | | Apple | Sign in with Apple | Arctic | | Magic Link | Email-based | Custom | The `auth_accounts` table records which providers are linked to an account, with `provider` one of `github`, `google`, `apple`, or `email`. ### OAuth Flow 1. User clicks "Sign in with GitHub" (or Google, or Apple). 2. The server generates a state parameter and redirects to the provider. 3. The provider authenticates the user and redirects back with an authorization code. 4. The server exchanges the code for an access token and retrieves the user profile. 5. If the email matches an existing user, the account is linked. Otherwise, a new user is created. 6. A session is created and returned as an HttpOnly cookie. 7. If the account has not accepted the current Terms of Service and Privacy Policy (a new account, or one signed in after either was updated), the web app shows an acceptance page before anything else. Until then, every `/api/*` call from that session returns `403` `terms_acceptance_required`, except reading your profile and deleting your account. The acceptance gate applies to **web sessions**. API keys and MCP tokens are not re-prompted when the documents change. Instead, the flows that create them are gated when the key is created. The two browser flows, `nrepo login` and connecting an MCP client, pass through the same acceptance page first. Native Sign in with Apple on iOS (`POST /auth/apple/native`) returns `403` `terms_acceptance_required` with the documents and no key, until the app sends the request again with `terms_accepted: true`. ### Magic Links For users who prefer email-based auth: 1. User enters their email address. 2. The server generates a one-time token, stores its SHA-256 hash in the `magic_links` table, and sends the token via email. 3. The user clicks the link, which contains the token. 4. The server hashes the token, looks up a record that is unexpired (15 minutes) and unused, and creates a session. 5. The record is **marked used** with a `used_at` timestamp, not deleted — a replayed link finds the row and is rejected. Sending links is rate-limited per UTC clock hour (the count resets on the hour, not an hour after the first link), so the sign-in form can't be used to flood an inbox: | Limit | Per hour | Counted by | |---|---|---| | Per inbox | 5 links | The address, lowercased, with any `+tag` removed. For Gmail addresses, dots are ignored too, so `j.doe+x@gmail.com` and `jdoe@gmail.com` share one count. The link still goes to the address exactly as typed. | | Per client IP | 20 links | The connecting IP address. IPv6 addresses are counted per `/64` network | Over either limit, the request returns `429` with `Retry-After: 3600` and `Too many sign-in links requested. Try again in an hour.` The answer is the same whether or not the address has an account. The counters live in KV and are not atomic, so a burst of simultaneous requests can slip a few past the limit. ## Session Management Sessions are the primary authentication mechanism for the web app. | Property | Value | |----------|-------| | Token format | 32 random bytes, hex-encoded (64 characters) | | Cookie name | `nrepo_session` | | Storage | SHA-256 hash stored in D1 `sessions` table | | Expiry | 30 days | | Cookie flags | `HttpOnly`, `Secure`, `SameSite=Lax`, `Path=/`, `Max-Age=2592000` | | Auto-refresh | Rolling — **every** authenticated request pushes expiry back to 30 days from now | The session is a **rolling** 30 days rather than a fixed one: any authenticated request extends it, so an account in daily use never expires, and one that goes quiet for a month does. Requests carrying the session as a `Bearer` token are refreshed in the database; requests carrying the cookie also get a fresh `Set-Cookie`. The raw session token is only ever sent to the client as a cookie. The server stores and compares only the SHA-256 hash. This means a database breach does not expose usable session tokens. **Note:** Session tokens are generated using the Web Crypto API (`crypto.getRandomValues`), ensuring cryptographic randomness. ## API Keys API keys provide programmatic access for the CLI, Siri Shortcuts, CI/CD, and custom integrations. | Property | Value | |----------|-------| | Format | `nrp_` prefix + 32 random bytes hex-encoded — 68 characters in total | | Storage | SHA-256 hash stored in D1 `api_keys` table | | Display | Never shown again; the settings list identifies keys by label and the last 4 characters of the key **record's id** | | Scopes | `NULL` on keys you create — full access. Scopes only exist on MCP OAuth tokens. | | Revocation | Immediate — the key record is deleted, and nothing is cached | When a request includes an `X-API-Key` header: 1. The header is ignored unless the value starts with `nrp_`. 2. The server hashes the provided key with SHA-256. 3. It looks up the hash in the `api_keys` table. 4. If found, the request is authenticated as the key's owner, with the key's scopes. 5. The `last_used_at` timestamp is updated. Credentials are checked in a fixed order: `Authorization: Bearer` (tried as a session token first, then as an API key or MCP token), then `X-API-Key`, then the session cookie. MCP OAuth tokens are not `nrp_`-prefixed, so they must be sent as `Bearer` — `X-API-Key` will not see them. The full key is shown **only once** at creation time. It cannot be retrieved later. **Note:** Creating an API key requires **Pro**, and the **Settings ▸ Connections** tab that manages keys and connected MCP clients is Pro-only. The exception is `nrepo login`, whose browser flow mints a key with no plan check — which is why the CLI works on Free. ## BYOK Encryption User-provided AI keys (Anthropic, OpenAI, OpenRouter) are encrypted before storage: | Property | Value | |----------|-------| | Algorithm | AES-256-GCM (Web Crypto) | | Key derivation | HKDF-SHA256 over the `ENCRYPTION_KEY` Workers secret, with a fixed salt and info string | | IV | Fresh random 12-byte IV per encryption | | Storage | A single base64 string of IV ‖ ciphertext ‖ auth tag in D1 | The secret is never used as an AES key directly — it is stretched through HKDF first, so the stored ciphertext does not depend on the secret's length or entropy distribution. The encryption key is a Workers secret that never appears in code or logs. Decryption happens in-memory only when making an AI request, and the plaintext key is never written to disk or returned via API. **Caution:** If the `ENCRYPTION_KEY` is rotated, all stored BYOK keys become unreadable. A migration script must re-encrypt all keys with the new key. ## MCP OAuth with PKCE The MCP integration uses a full **OAuth 2.0 with PKCE** flow to authorize Claude's access: | Property | Value | |----------|-------| | Grant type | Authorization Code with PKCE, plus `refresh_token` | | Challenge method | **S256 only, and required.** A missing `code_challenge`, a missing `code_challenge_method`, `plain`, or a challenge that isn't 43 base64url characters (a SHA-256 hash) is refused with `invalid_request` | | Client registration | Dynamic Client Registration (RFC 7591) at `/mcp/register`, kept 30 days | | Authorization code expiry | 10 minutes, single use | | Access token expiry | 30 days | | Refresh token expiry | 90 days, rotated on every refresh | | Scopes | `ideas:read`, `ideas:write` | The PKCE flow prevents authorization code interception attacks. The MCP client generates a random `code_verifier`, hashes it to create a `code_challenge`, and sends the challenge with the authorization request. When exchanging the code for tokens, the server verifies the original verifier matches the stored challenge. Every **new** authorization must use PKCE with `code_challenge_method=S256` stated explicitly. `GET /mcp/authorize` answers anything else with `400` `invalid_request`, and the consent form redirects back with `error=invalid_request` and no code. Refresh grants carry no PKCE, so a client that is already connected keeps refreshing. The server metadata at `/.well-known/oauth-authorization-server` advertises `code_challenge_methods_supported: ["S256"]`. Authorization codes and refresh tokens are both stored as SHA-256 hashes, in `oauth_authorization_codes` and `oauth_refresh_tokens`. Scopes are enforced at the tool boundary: every MCP tool that writes requires `ideas:write`, and a token carrying neither scope is refused. **Caution:** Connected MCP clients are revoked in **Settings ▸ Connections**, which is a **Pro-only** tab. A Free account that has authorized an MCP client has no in-app way to revoke it. ## CORS Middleware The API uses strict CORS configuration: | Header | Value | |--------|-------| | `Access-Control-Allow-Origin` | The request origin if it is `https://neuralrepo.com` or any `*.neuralrepo.com` subdomain; otherwise `APP_URL` | | `Access-Control-Allow-Methods` | `GET, POST, PATCH, DELETE, OPTIONS` | | `Access-Control-Allow-Headers` | `Content-Type, Authorization, X-API-Key` | | `Access-Control-Allow-Credentials` | `true` | | `Access-Control-Max-Age` | `86400` (24 hours) | Reflection is limited to NeuralRepo's own domains — an unrecognized origin is answered with the app URL rather than itself, so the browser blocks it. Non-production environments additionally allow `*.workers.dev` (staging) and `http://localhost:*`. **Note:** `PUT` is **not** in the allowed-methods list, even though `PUT /user/byok/:provider` exists. The dashboard calls it same-origin, so no preflight is involved; a cross-origin browser client would be blocked. Server-side callers are unaffected — CORS is a browser mechanism, and API key-authenticated requests (CLI, Siri, CI) are not subject to it. ## Rate Limiting Rate limits are enforced using **Cloudflare KV** as a daily counter store. Limits apply only to requests authenticated via **API key** (`X-API-Key` header) or **Bearer token**. Web UI sessions are exempt. | Plan | Daily Limit | |------|-------------| | Free | 100 requests/day | | Pro | 10,000 requests/day | When a limit is exceeded, the API returns `429 Too Many Requests` — with **no `Retry-After` header**. The counter key format is `rate:{user_id}:{YYYY-MM-DD}` with a 24-hour TTL. The date is UTC, so counters reset at UTC midnight. The limit is per user, not per key: every API key, MCP client, and CLI install on one account shares the same budget. ## Data Isolation Every user-facing database query in NeuralRepo is scoped by `user_id`: ```sql SELECT * FROM ideas WHERE user_id = ? AND is_archived = 0 ``` Each user's data is isolated — you cannot access, search, or modify another user's ideas regardless of the authentication method. Ownership is checked on the read, not just at the route: fetching an idea by id requires the id **and** the user id to match, so guessing an id gets you a 404. **Note:** Vectorize queries are also filtered by `user_id` metadata, ensuring semantic search results only return the authenticated user's ideas. **Note:** One surface is deliberately account-wide: `/api/v1/admin`, gated to a **single** account id configured as `ADMIN_USER_ID`. Any other authenticated user gets `403`. It exposes aggregate counts — totals by status, source, and plan, billing sums, active session counts — and no idea titles or bodies. ## Security Headers Every response carries a **Content-Security-Policy**, set by middleware that rebuilds the response so the header lands even on immutable responses passed through from the static-assets binding. The Worker is deliberately configured to run *before* asset serving for exactly this reason — otherwise the SPA's own HTML would be returned without a CSP. | Directive | Value | |---|---| | `default-src` | `'self'` | | `script-src` | `'self'` — no inline scripts, no CDNs | | `style-src` | `'self' 'unsafe-inline'` plus Google Fonts | | `font-src` | `'self'` plus Google Fonts | | `img-src` | `'self'`, `data:`, `blob:`, and the Google/GitHub avatar hosts | | `connect-src` | `'self'` plus Stripe | | `frame-src` | `'self'` plus Stripe (checkout) | | `object-src` | `'none'` | | `base-uri` | `'self'` | ## Next Steps - [API Authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): Using each credential type against the REST API, with examples. - [MCP Server](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): The OAuth flow from the client side, and how to revoke access. - [Data Model](https://docs.neuralconfig.com/neuralrepo/architecture/data-model/): The tables behind sessions, keys, and OAuth grants. - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): The daily budget, what counts against it, and what a 429 looks like. --- # Semantic Search Source: https://docs.neuralconfig.com/neuralrepo/architecture/semantic-search/ NeuralRepo goes beyond keyword matching. Every idea is converted into a vector embedding that captures its **meaning**, enabling search by concept rather than exact words. ## Embedding Model NeuralRepo uses the **`@cf/baai/bge-m3`** model running on Cloudflare Workers AI: | Property | Value | |----------|-------| | Model | `@cf/baai/bge-m3` | | Dimensions | 1024 | | Multilingual | Yes (100+ languages) | | Input truncated at | 8,192 characters (~2,048 tokens) before the call | | Timeout | 30 seconds, after which the queue message is retried | | Runtime | Cloudflare Workers AI | The BGE-M3 model was chosen for its strong multilingual performance, high dimension count for nuanced similarity, and native availability on Cloudflare's infrastructure. ## Vector Storage Embeddings are stored in **Cloudflare Vectorize** (index `neuralrepo-ideas`): | Setting | Value | |---------|-------| | Metric | Cosine similarity | | Dimensions | 1024 | | Vector ID | `idea_` — the database id, not the display number | | Metadata | `user_id`, `idea_id`, `status`, `tags` (comma-joined), `source`, `created_at` | Metadata is stored alongside each vector to enable filtered queries — every query filters on `user_id`, and search adds `status` and tag filters on top. Tags get vectors too, under `tag--`, embedded from the tag name plus the titles of five of its ideas. ## Embedding Pipeline When a new idea is created, the embedding process runs asynchronously: ```mermaid flowchart TD created["Idea created"] --> msg["Queue message
(idea_created)"] msg --> norm["Text normalization
join title + body · trim · collapse whitespace · truncate to 8,192 chars"] norm --> embed["Workers AI embedding
@cf/baai/bge-m3 → 1024-dim vector"] embed --> dup["Duplicate detection
query topK=5 · exclude shelved · write detections + related links"] dup --> tag["Auto-tag
only if the idea has no tags"] tag --> upsert["Vectorize upsert
id idea_138 · 1024 floats · metadata incl. tags"] upsert --> done["Store vectorize_id on the idea"] ``` The order matters: duplicate detection runs against the **query embedding before the new vector is stored**, so an idea is never its own nearest neighbour, and auto-tagging happens **before** the upsert so the vector's `tags` metadata is complete. **Note:** Embedding is asynchronous. The API returns `"processing": true` immediately. Semantic search results for the new idea are available within a few seconds. Until then the idea's `vectorize_id` is null — that field, not `processing`, is how you tell the pipeline finished. ## Search Pipeline Semantic search is **not** a hybrid merge of keyword and vector results. It is a vector search with a reranker, and keyword search only appears as a fallback: ```mermaid flowchart TD q["Search query"] --> embed["Embed the query
@cf/baai/bge-m3"] embed --> vec["Vectorize query
topK = limit x 3, clamped to 20-50 · filter user_id"] vec --> filter["Drop archived · apply status/tag filters
drop anything at or below min_score"] filter --> any{"Any survivors?"} any -->|Yes| rerank["Rerank
@cf/baai/bge-reranker-base · keep top limit"] any -->|No| fts["FTS5 keyword search
fetch limit x 2 · filter · slice to limit"] rerank --> out["search_type: semantic"] fts --> out2["search_type: fts_fallback"] ``` In words: the query is embedded, Vectorize returns between 20 and 50 candidates filtered to your own ideas, archived ideas and anything failing your status or tag filter are dropped, and everything scoring at or below `min_score` (default 0.2, or your `search_threshold` setting) is discarded. What survives is reranked by `@cf/baai/bge-reranker-base` and truncated to your limit. If nothing survives — or the vector search errored — keyword search over the FTS5 index answers instead. Two details worth knowing: - **The reranker is skipped when there is nothing to rerank.** With fewer candidates than the requested limit, results are returned in score order without a model call. If the rerank call fails, results also degrade to score order rather than falling back to keyword search. - **The keyword fallback is per-surface.** Only the MCP tool and the AI agent fall back automatically. The REST API, the CLI, and the web app return an empty result set instead. See [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/) for the full table. ## Duplicate Detection When a new idea is embedded, NeuralRepo checks its five nearest neighbours — excluding ideas in `shelved` status — and acts on each: | Score | Action | |-----------|--------| | **> 0.75** (dedup threshold) | A `duplicate_detections` row with `pending` status — **and**, because the score also clears the related threshold, a system-created `related` relation | | **> 0.55** (related threshold) | A system-created `related` relation | | **≤ 0.55** | No action | **Note:** The bands are not exclusive. A pair above 0.75 gets **both** a duplicate detection and a `related` relation — the two checks are independent comparisons against the same match. Both comparisons are strictly greater-than, so a score of exactly 0.75 is not a duplicate. Both thresholds are per-user settings (`dedup_threshold` and `related_threshold`), each clamped to 0.1–0.9. Detections are written with `INSERT OR IGNORE` against a unique index on the idea pair, so re-running detection cannot duplicate a detection. **Tip:** Reviewing detections requires Pro — on Free, `GET /ideas/duplicates` always returns an empty list even though detection itself still runs and still creates the rows. ## Text Normalization Before embedding, text is normalized: 1. **Combine fields.** Title and body are joined with a **space**: `{title} {body}`. 2. **Trim.** Leading and trailing whitespace is removed. 3. **Collapse whitespace.** Runs of spaces, tabs, and newlines collapse to a single space — so Markdown structure contributes nothing to the embedding. 4. **Truncate.** The result is cut to **8,192 characters**. A 50,000-character body is embedded from its first ~8,000 characters only. Ideas with only a title are still embedded. An idea that normalizes to an empty string throws, which sends the queue message to a retry. ## Re-embedding When an idea's title or body is updated, a new embedding is generated: 1. An `idea_updated` queue message is dispatched. 2. The consumer re-normalizes and re-embeds the updated text. 3. System-created relations and pending duplicate detections for the idea are **deleted**, then re-derived from the new embedding. 4. The existing Vectorize record is upserted (replaced) with the new vector. Metadata-only changes (status, tags) dispatch an `idea_metadata_updated` message that rewrites the Vectorize metadata using the stored vector, with no model call. Archiving an idea **deletes its vector** (`VECTORIZE.deleteByIds(["idea_"])`), which is why an archived idea stops appearing as anyone's neighbour immediately. Restoring an idea (`POST /ideas/:id/restore`) clears `is_archived` and enqueues an `idea_updated` message, so the vector is rebuilt rather than recovered. ## Backfill Two queue messages rebuild vectors: - **`backfill_vectors`** re-enqueues every unarchived idea for a user as `idea_created`, replaying the whole pipeline. - **`backfill_tag_vectors`** force-re-embeds the user's tag vectors, 20 at a time. An hourly cron also repairs individual gaps on its own, re-queueing up to 50 ideas that have no `vectorize_id` more than five minutes after creation. In practice that catches the failure these backfills exist for, without anyone asking. **Note:** Both backfills are triggered by the maintainer, not by an API you can call. The admin surface is gated to a single account id and exposes aggregate statistics only. ## Next Steps - [Queue Processing](https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/): The full pipeline this page's embedding step lives in. - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): Searching from each surface, including where the fallback is. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): Working through detections, merging, and tuning the thresholds. - [Search API](https://docs.neuralconfig.com/neuralrepo/api/search/): GET /ideas/search — parameters, scoring, and response shape. --- # Tech Stack Source: https://docs.neuralconfig.com/neuralrepo/architecture/tech-stack/ NeuralRepo is an **edge-first** application built entirely on the Cloudflare developer platform. Every request is handled at the edge, keeping latency low regardless of where you are in the world. ## Cloudflare Services **Workers + Hono** The API server runs on Cloudflare Workers using the **Hono** framework. Hono provides Express-like routing with middleware support, optimized for the Workers runtime. Every API endpoint, MCP handler, and OAuth flow runs here — and the Worker is configured to run *before* static assets so security headers apply to the SPA's HTML too. **D1 (SQLite)** All structured data — users, ideas, tags, relations, sessions, billing events — lives in **Cloudflare D1**, a serverless SQLite database at the edge. Includes an FTS5 virtual table for full-text search. **Vectorize** Semantic search and duplicate detection are powered by **Cloudflare Vectorize** (index `neuralrepo-ideas`), storing 1024-dimension embeddings alongside metadata. Uses cosine similarity for nearest-neighbor queries. **Queues** Asynchronous processing — embedding generation, duplicate detection, auto-tagging — is handled by **Cloudflare Queues**, in batches of up to 10 with three retries and a dead-letter queue. Ideas are processed in the background so API responses return immediately. **KV** **Cloudflare KV** holds short-lived counters and registrations: daily rate-limit counters, the free plan's monthly semantic-search counter, the stale-nudge cooldown, and dynamically registered MCP OAuth clients. Every key is TTL'd. **R2** Object storage via **Cloudflare R2**. Today the `ATTACHMENTS` bucket serves exactly one thing — the demo video on the marketing page. User file attachments are not implemented. **Workers AI** Used for embeddings (`@cf/baai/bge-m3`), search reranking (`@cf/baai/bge-reranker-base`), the two keyless agent models (GPT-OSS 120B, Qwen 30B), and the agent's sub-agents (IBM Granite 4.0 H Micro for utility work, Qwen 30B for creative work) — none of which need a user API key. **AI Gateway** All AI requests — BYOK Anthropic calls and Workers AI calls alike — are routed through **Cloudflare AI Gateway**, providing logging, caching, and rate limiting. **Durable Objects** The **AI agent** (`NeuralRepoAgent`) runs as a Cloudflare **Durable Object**, giving each user a persistent, stateful WebSocket session. Conversation history is stored in the Durable Object's SQLite storage. **Worker Loaders** Code mode — both the MCP `code_mode` tool and the agent's — executes user-authored JavaScript in a dynamically loaded, isolated Worker via the `LOADER` binding. **Email Routing** Mail to `idea@neuralrepo.com`, including every personal `idea+@` address (subaddressing), is routed to the Worker's `email` handler. It parses the message into an idea, or rejects it at SMTP time. The confirmation for a captured idea is a reply to the incoming message (`message.reply()`); refusals get no reply. The `send_email` binding is configured but not used. **Cron Triggers** Four schedules: the Sunday weekly digest, Monday stale-idea nudges, a daily 04:00 UTC run of the trial-expiry and data-retention sweeps, and an hourly job that re-queues ideas whose background processing never ran. ## Backend Dependencies The Worker itself has a deliberately short dependency list: | Package | Purpose | |---|---| | `hono` | HTTP routing and middleware | | `arctic` | GitHub, Google, and Apple OAuth flows | | `zod` | Request validation, shared with the CLI and frontend | | `ai` + `@ai-sdk/anthropic` + `workers-ai-provider` | Agent model calls across Anthropic and Workers AI | | `agents` | The `AIChatAgent` base class behind the Durable Object | | `@cloudflare/codemode` | Sandboxed code-mode execution | | `stripe` | Billing and webhook signature verification | | `fflate` | Zipping multi-file exports | ## Frontend The web dashboard is a **React 19 SPA** built with **Vite**, with hand-rolled routing and plain CSS — no router library, no CSS framework, and no data-fetching library: | Technology | Purpose | |-----------|---------| | React 19 | UI framework | | Vite | Build tool and dev server | | `3d-force-graph` + `three` | 3D mind map visualization | | `d3` | 2D force-directed mind map | | `@dnd-kit/*` | Drag and drop for the kanban board and the mind map | | `react-markdown` + `remark-gfm` | Rendering idea bodies and agent replies | | `rehype-highlight` + `rehype-sanitize` + `dompurify` | Syntax highlighting and sanitizing rendered Markdown | | `mermaid` | Diagram rendering inside idea bodies and agent output | The SPA is served via Cloudflare Workers static assets and communicates with the Workers API. ## CLI The `@neuralconfig/nrepo` CLI is built with: | Technology | Purpose | |-----------|---------| | Commander.js | Command parsing and help generation | | Chalk | Terminal styling | | Ora | Spinner animations | | Zod | Response validation | Configuration is a plain JSON file at `~/.config/neuralrepo/config.json`, written with `node:fs` — there is no config library and no OS keychain integration. The CLI is published to npm and communicates with the same REST API. ## Shared Library The `@neuralrepo/shared` package contains code shared between the API, frontend, and CLI: - **Zod schemas** for request and settings validation - **TypeScript types** inferred from those schemas - **Constants** — the status, source, link-type, and relation-type enums - **`LIMITS`**, the single source of truth for field limits enforced across API, MCP, and CLI This ensures type safety and validation consistency across all three surfaces. ## Why Cloudflare NeuralRepo chose the Cloudflare platform for three reasons: 1. **Edge latency.** Every API call runs at the nearest Cloudflare edge location. Whether you are in Tokyo or Toronto, response times are consistently low. 2. **Unified billing.** All services (compute, storage, vectors, AI, queues) are under one account with predictable pricing. No cross-provider networking costs. 3. **Integrated tooling.** D1, Vectorize, Queues, and Workers AI are first-class bindings in the Workers runtime. No SDKs, no connection strings — just direct bindings with TypeScript types. **Note:** The entire NeuralRepo backend is a single Workers project with bindings to all services. There are no microservices, no containers, and no servers to manage. ## Architecture Diagram Three clients — the React web app, the Node.js CLI, and the remote Claude MCP connector — all reach the same Workers (Hono) API, which binds D1, KV, R2, the agent Durable Object, and the Queue directly. Background queue jobs call Workers AI and Vectorize; Workers AI and BYOK Anthropic calls both go through AI Gateway. ```mermaid flowchart TD web["Web App
(React)"] --> workers["Workers
(Hono)"] cli["CLI
(Node.js)"] --> workers mcp["Claude MCP
(Remote)"] --> workers workers --> d1[("D1
SQLite")] workers --> kv[("KV
Counters")] workers --> r2[("R2
Demo asset")] workers --> agent["Durable Object
NeuralRepoAgent"] workers --> queues["Queues
(Background)"] agent --> gateway["AI Gateway"] queues --> ai["Workers AI"] ai --> gateway queues --> vec[("Vectorize")] ``` ## Next Steps - [Data Model](https://docs.neuralconfig.com/neuralrepo/architecture/data-model/): Every table in D1, with the columns that matter. - [Queue Processing](https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/): What the background pipeline does with each message type. - [Semantic Search](https://docs.neuralconfig.com/neuralrepo/architecture/semantic-search/): The embedding model, Vectorize configuration, and search path. - [Security & Auth](https://docs.neuralconfig.com/neuralrepo/architecture/security/): Sign-in methods, credential storage, CORS, and rate limiting. --- # REST API Source: https://docs.neuralconfig.com/neuralrepo/capture/api/ The NeuralRepo REST API gives you full programmatic access to your ideas. Build custom integrations, automate idea capture from other tools, or create your own front-end — the API supports everything the web dashboard does. ## Base URL ``` https://neuralrepo.com/api/v1 ``` All endpoints are served over HTTPS. HTTP requests are rejected. ## Authentication Authenticate every request with your API key in the `X-API-Key` header: ``` X-API-Key: nrp_your_api_key_here ``` Generate an API key in **Settings > API Keys** on the web dashboard. Keys use the format `nrp_` followed by 64 hexadecimal characters. **Caution:** Keep your API key secret. It grants full read-write access to your NeuralRepo account. Do not commit it to version control or share it publicly. ## Quick Example: Create an Idea **curl** ```bash curl -X POST https://neuralrepo.com/api/v1/ideas \ -H "Content-Type: application/json" \ -H "X-API-Key: nrp_your_api_key_here" \ -d '{ "title": "Build a personal finance dashboard", "body": "Track spending, budgets, and investments in one place. Use Plaid for bank integration.", "tags": ["fintech", "side-project"], "status": "exploring" }' ``` **JavaScript (fetch)** ```javascript const response = await fetch("https://neuralrepo.com/api/v1/ideas", { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": "nrp_your_api_key_here", }, body: JSON.stringify({ title: "Build a personal finance dashboard", body: "Track spending, budgets, and investments in one place. Use Plaid for bank integration.", tags: ["fintech", "side-project"], status: "exploring", }), }); const idea = await response.json(); console.log(idea); ``` **Python (requests)** ```python import requests response = requests.post( "https://neuralrepo.com/api/v1/ideas", headers={ "Content-Type": "application/json", "X-API-Key": "nrp_your_api_key_here", }, json={ "title": "Build a personal finance dashboard", "body": "Track spending, budgets, and investments in one place. Use Plaid for bank integration.", "tags": ["fintech", "side-project"], "status": "exploring", }, ) idea = response.json() print(idea) ``` ### Request Body | Field | Type | Required | Description | |-------|------|:--------:|-------------| | `title` | string | Yes | Idea title (1-200 characters) | | `body` | string | No | Idea body in plain text or Markdown | | `tags` | string[] | No | Array of tags (max 20) | | `status` | string | No | One of: `captured`, `exploring`, `building`, `shipped`, `shelved`. Defaults to `captured` | | `source` | string | No | Capture channel to record: `web`, `cli`, `claude-mcp`, `siri`, `email`, `api`, `shortcut`, `ios` (reserved for the iOS app, which is not yet released). Defaults to `web`, so send `"source": "api"` explicitly if you want API captures labelled | | `source_url` | string | No | URL where the idea originated (max 2,000 characters) | | `parent_id` | number | No | Parent idea ID. Background processing turns this into a `parent` relation | Bodies are capped at 50,000 characters and titles at 200. ### Response A successful request returns `201 Created` with the full idea object: ```json { "id": 42, "title": "Build a personal finance dashboard", "body": "Track spending, budgets, and investments in one place. Use Plaid for bank integration.", "status": "exploring", "source": "api", "tags": ["fintech", "side-project"], "processing": true, "created_at": "2026-03-24T14:22:00Z", "updated_at": "2026-03-24T14:22:00Z" } ``` **Tip:** The `processing: true` field indicates that background tasks are still running — embedding generation for semantic search and duplicate detection. The idea is fully saved and accessible immediately; processing happens asynchronously. ## Error Responses The API returns standard HTTP status codes with a JSON error body. Validation failures put the offending fields in `error`, one array of messages per field: ```json { "error": { "title": ["String must contain at most 200 character(s)"] } } ``` | Status Code | Meaning | |:-----------:|---------| | `400` | Bad request — invalid or missing parameters | | `401` | Unauthorized — missing or invalid API key | | `403` | Forbidden — action not allowed (e.g., free tier limit reached) | | `404` | Not found — idea or resource does not exist | | `429` | Rate limited — daily request quota exceeded | | `500` | Server error — try again or contact support | Hitting the free plan's idea limit returns a `403` with a machine-readable shape you can branch on: ```json { "error": "idea_limit_reached", "message": "You've captured 50 ideas on the free plan. Upgrade to Pro for unlimited ideas.", "currentCount": 50, "limit": 50, "upgradeUrl": "https://neuralrepo.com/upgrade", "featureRequestUrl": "https://support.neuralrepo.com/feature-requests" } ``` Pro-only endpoints return `403` with `pro_required: true` and the feature name. ## Rate Limits API endpoints are rate-limited per user per day. Free plans allow **100 requests per day**, and Pro plans allow **10,000 requests per day**, counted per UTC day and reset at midnight UTC. If you exceed this, the API returns `429 Too Many Requests`. Rate limits apply to API key and Bearer token requests only — web UI sessions are exempt. Multiple keys share one quota, and MCP calls count against it too. ## Next Steps - [API Reference](https://docs.neuralconfig.com/neuralrepo/api/overview/): Complete documentation for all API endpoints — ideas, relations, links, tags, search, and more. - [Authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): Key formats, Bearer sessions, and how to rotate a key. - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): Quotas per plan, what counts, and how to read a 429. - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): The search endpoint's modes, thresholds, and fallback behaviour. --- # CLI Source: https://docs.neuralconfig.com/neuralrepo/capture/cli/ The NeuralRepo CLI (`nrepo`) lets you capture and manage ideas without leaving your terminal. It is fast, scriptable, and works seamlessly with developer workflows — including Claude Code. ## Why Use the CLI? - **Fast capture** — push an idea in a single command, no context switching - **Scriptable** — pipe output, use in shell scripts, integrate with CI/CD - **Works with Claude Code** — Claude Code can invoke `nrepo` commands directly in your development workflow - **JSON output** — parse results programmatically for automations ## Installation Install the CLI globally via npm: ```bash npm install -g @neuralconfig/nrepo ``` Verify the installation: ```bash nrepo --version ``` **Caution:** The CLI requires Node.js 18 or later. If you encounter permission errors, see [npm's guide to fixing global install permissions](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally). ## Authentication **OAuth (recommended)** Log in using your browser — this is the easiest method: ```bash nrepo login ``` Your browser opens **GitHub** sign-in, and the server hands a freshly minted API key back to the CLI. It is stored in `~/.config/neuralrepo/config.json`. This flow works on **every plan**, so the CLI is usable without Pro. **API Key** For headless environments, CI/CD, scripting — or if you sign in to NeuralRepo with Google or a magic link rather than GitHub — paste a key instead: ```bash nrepo login --api-key # Paste your API key when prompted: nrp_your_api_key_here ``` `--api-key` is a bare flag: it prompts, it does not take the key as an argument. Generate the key in **Settings ▸ API Keys** on the web dashboard (a Pro feature). Keys use the format `nrp_` followed by 64 hexadecimal characters. ## Quick Push Capture an idea in a single command: ```bash nrepo push "Build a habit tracker app" --body "Mobile-first, with streak tracking and reminders" --tag side-project --status exploring ``` All flags are optional except the title (the first positional argument): | Flag | Description | Default | |------|-------------|---------| | `--body ` | Idea body text (Markdown) | *(empty)* | | `--tag ` | Add a tag (repeatable) | *(none)* | | `--status ` | Set initial status | `captured` | | `--json` | JSON output | *(human-readable)* | | `--human` | Human-readable output — wins over `--json` | *(default)* | The body is passed as a flag value, so quote it. Multi-line bodies work with a quoted string or your shell's line continuation; there is no stdin mode. ### Example with multiple tags ```bash nrepo push "AI-powered code review tool" \ --tag ai \ --tag devtools \ --tag saas \ --status exploring ``` ## Output Modes Every command prints **human-readable** output by default, on any authentication method. Pass `--json` for machine-parseable output — including errors, which become a single JSON object on stderr with an `error`, a `code`, and (for API failures) a `status`. ```bash # JSON output (useful for scripting) nrepo push "My idea" --json # --human wins if both are passed nrepo search "tracker" --json --human ``` ### Human-readable output Ideas print as one row each: number, status, a glyph for the capture source, title, tags, and — for search results — the relevance score. ``` ✓ Idea captured #42 exploring ⬡ AI-powered code review tool [ai, devtools, saas] Processing: embeddings, dedup, and auto-tagging queued ``` ### JSON output ```json { "id": 42, "title": "AI-powered code review tool", "status": "exploring", "tags": ["ai", "devtools", "saas"], "source": "cli", "processing": true, "created_at": "2026-03-24T10:30:00Z" } ``` **Tip:** The `processing: true` field indicates that background tasks (embedding generation, duplicate detection) are still running. The idea is saved and available immediately. ## Common Commands ```bash # Search ideas nrepo search "habit tracker" # List recent ideas nrepo log # View a specific idea nrepo show 42 # Change an idea's status nrepo move 42 building # Delete an idea nrepo rm 42 ``` ## Configuration The CLI stores its configuration at: ``` ~/.config/neuralrepo/config.json ``` This file contains your authentication credentials (OAuth token or API key). Do not share it or commit it to version control. ## Full Reference - [CLI Command Reference](https://docs.neuralconfig.com/neuralrepo/cli/installation/): Complete documentation for every nrepo command, flag, and option. ## Next Steps - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): What nrepo search actually runs, and when it returns nothing rather than falling back. - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): Tag one idea or many, and what auto-classification does with an untagged capture. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): Review and merge duplicates from the terminal with nrepo duplicate. - [REST API](https://docs.neuralconfig.com/neuralrepo/capture/api/): The same surface the CLI drives — useful for scripts in other languages. --- # Email Capture Source: https://docs.neuralconfig.com/neuralrepo/capture/email/ NeuralRepo lets you capture ideas by sending a simple email. No app to open, no login required — just compose and send. ## Where do I send ideas? Send them to your **personal capture address**, which looks like this: ``` idea+<24 hex characters>@neuralrepo.com ``` Find it in the web app under **Settings ▸ Integrations ▸ Email capture**. The address is created the first time you open that card, and it stays the same until you replace it. The address itself is the credential. Any mail that reaches it becomes an idea in your repository, **whoever sent it and from whatever mailbox**. So you can send from a work alias or a second address, or forward mail through a service that rewrites the sender, and it still lands. It also means you should keep the address private, as the Settings card says: anyone who has it can add ideas to your repo. | Email Field | Idea Field | |-------------|------------| | **Subject** | Title — or the first line of the body if the subject is empty | | **Body** | Body (plain text, signature stripped) | | **To address** | Picks the account — the token after `idea+` | The idea is created with: - **Status**: `captured` - **Source**: `email` - **Tags**: none at first — auto-tagging fills them in a few seconds later **Tip:** Email capture is designed for quick, low-friction idea saving. Write a clear subject line — it becomes your idea title. Put details, context, or notes in the body. ## What happens to the mail I send? A message is either **captured** or **rejected while it is being delivered**. NeuralRepo does not send a reply explaining a refusal. The rejection is an SMTP-time reject, so your own mail provider sends you a bounce (a non-delivery notice), which usually quotes NeuralRepo's reason. | You sent to | Result | |-------------|--------| | Your personal address, on a Pro account | **Captured.** A confirmation is sent back to the sender, with the title, the idea's `#number`, and a link to open it | | Your personal address, but the mail was empty | Still captured; the title falls back to the first line of the body, or `Untitled idea (from email)` | | Your personal address, on a Free account | Rejected: `Email capture is a NeuralRepo Pro feature` | | An `idea+…` address that has been replaced, or never existed | Rejected: `Unknown NeuralRepo capture address` | | The shared `idea@neuralrepo.com` | Rejected, unless you opted in during the transition (see below): `Send ideas to your personal capture address, shown in NeuralRepo Settings (idea+...@neuralrepo.com)` | **Note:** The confirmation is best-effort. Cloudflare refuses to send a reply to a message that has no valid DMARC result, so some senders get the idea but no confirmation. Check the web app or `nrepo log` if a confirmation doesn't arrive. If you heard nothing at all, neither a confirmation nor a bounce, the mail most likely never reached NeuralRepo. Check your sent folder, and check the address against the one in Settings. If you get the confirmation and see the idea but can't find its tags yet, give it a few seconds: tagging runs in the background. ## How do I set it up? 1. **Open the Email capture card** In the web app, go to **Settings ▸ Integrations** and find the **Email capture** card. On a Free plan the card is titled **Email capture — Pro** and shows an upgrade link instead of an address. 2. **Copy your address** Click **Copy** next to your `idea+…@neuralrepo.com` address, and save it as a contact in your mail app so you don't have to type it. 3. **Send an email** - **To**: your personal capture address - **Subject**: Your idea title (e.g., "Gamified onboarding flow for new users") - **Body**: Any additional details or context 4. **Check your ideas** The idea appears in your NeuralRepo dashboard within a few seconds, ready for triage. ## How do I get a new address? Click **New address** on the Email capture card and confirm. The old address **stops working immediately**: mail sent to it afterwards is rejected with `Unknown NeuralRepo capture address`. Update the saved contact in your mail app. Replace the address whenever you think someone else has it. Deleting your account clears the address as well. ## What about the old idea@neuralrepo.com address? Until **October 24, 2026** (00:00 UTC), you can opt in to keep using the shared address during the move to personal addresses. After that date the shared address rejects everything. - **The opt-in is a checkbox on the Email capture card:** *Also accept mail from my account email to the shared idea@neuralrepo.com until…*. It is shown only until the cut-off, and it is off unless you tick it. - **It works only from your account email.** The shared address can't tell who a message is for, so it matches the **From** address, lowercased, against your account email. Mail from any other address is rejected. - **It needs a Pro plan** at the moment the mail arrives. - **It sends no confirmation.** The shared address can't verify who sent a message, so it never replies. The idea simply appears. **Caution:** If you opt in, don't send the same message to both addresses. Mail addressed to your personal address **and** `idea@neuralrepo.com` is delivered twice, and each copy creates an idea. ## Requirements - A NeuralRepo **Pro** plan, checked when each message arrives - Mail sent to your personal capture address from **Settings ▸ Integrations ▸ Email capture** ## How the Body Is Cleaned Up The handler keeps the readable text and throws away the mail plumbing: - **Multipart messages** use the `text/plain` part when there is one, and convert the HTML part when there isn't — paragraphs, line breaks, and list items become newlines, and HTML entities are decoded. - **Signatures and quoted replies are cut**, along with everything after them. The cut triggers on a `--` delimiter line, `Sent from my …`, `Get Outlook for …`, a long line of underscores, or an `On … wrote:` quote header. **Caution:** That last rule is the one that surprises people: **forwarding an email keeps only the text above the quoted original**. If you forward an article or a colleague's mail, write what you want to keep at the top — everything from the `On … wrote:` line down is discarded. ## Use Cases - **Capture ideas from your phone** without installing anything — use your default email app - **Capture from any device** — any device that can send email can capture ideas - **Comment on something you were sent** — forward it and put your thinking at the top, above the quoted text ## Tips - **Write descriptive subjects** — the subject line is your idea title, so make it clear and specific - **Lead with your own words** — anything below a quoted reply or signature is stripped - **One idea per email** — each email creates one idea. For multiple ideas, send multiple emails - **Add context later** — email capture is intentionally minimal. Add tags, relations, and detail from the web or CLI ## Limitations - Only text is kept. Images and file attachments are not processed. - There is no way to set tags or status via email — every email-captured idea starts as `captured`, and is auto-tagged afterwards like any other capture. - Non-ASCII subjects are decoded from RFC 2047 encoded words; anything the decoder cannot read arrives verbatim in the title. ## Next Steps - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): What auto-classification does with an email that arrives with no tags. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): Triage what you emailed in — quiet ideas, new connections, possible duplicates. - [Weekly Digest](https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/): The other side of email: what NeuralRepo sends you, and when. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): Email capture is Pro — what else the plan includes. --- # iOS App Source: https://docs.neuralconfig.com/neuralrepo/capture/ios/ The NeuralRepo iOS app is a native SwiftUI client for the two things you do on a phone: capture an idea the moment it arrives, and find one when you need it. It talks to the same `/api/v1` surface as the [CLI](https://docs.neuralconfig.com/neuralrepo/capture/cli/), authenticated with the same `nrp_` key, so everything you capture appears everywhere else immediately. **Not yet released:** **The NeuralRepo iOS app is not released, and you can't install it today.** There is no App Store or TestFlight listing, and its source is not public, so the build steps below only work for the NeuralRepo team. The rest of this page describes the app as it is built for testing, and it may change before release. **On iPhone today**, use [Siri Shortcuts](https://docs.neuralconfig.com/neuralrepo/capture/siri/) for voice capture, [email capture](https://docs.neuralconfig.com/neuralrepo/capture/email/) from any mail app, or the [web app](https://docs.neuralconfig.com/neuralrepo/capture/web/) in Safari. ## Installing 1. Install the prerequisites: ```bash brew install xcodegen # Xcode 16 or later is also required ``` 2. Generate the Xcode project from the checked-in definition: ```bash cd ios xcodegen generate open NeuralRepo.xcodeproj ``` The `.xcodeproj` is generated and git-ignored — edit `project.yml` and re-run `xcodegen generate` rather than the project file. 3. In Xcode, select your Apple Developer team under **Signing & Capabilities** for all three targets: **NeuralRepo**, **ShareExtension**, and **NeuralRepoWidget**. They share an App Group and a Keychain access group, both already declared in `project.yml`. 4. Build to a device with ⌘R. **Danger:** **Debug builds talk to staging.** The backend origin is chosen at compile time: ⌘R from Xcode points the app at `staging.neuralrepo.com`, while a Release build (Archive, TestFlight, App Store) points at `neuralrepo.com`. Ideas captured from a Debug build land in your staging account, not your real one. Archive the app rather than running it if you want your own data. ## Signing in The sign-in screen offers two paths, and both end with an `nrp_` API key in the shared Keychain: - **Sign in with Apple** — native, on-device. The identity token is posted to `/auth/apple/native` and exchanged for a key. - **Sign in** (web) — opens `/auth/cli` in a `ASWebAuthenticationSession`, the same endpoint `nrepo login` uses, and returns to the app at `neuralrepo://auth`. If your account hasn't accepted the current Terms of Service and Privacy Policy, **Sign in with Apple** first shows a sheet linking the Terms, the Privacy Policy and the NeuralRepo privacy addendum, an agreement toggle, and **Accept and continue** and **Cancel**. **Accept and continue** stays disabled until the toggle is on. Cancelling leaves you signed out. The web path shows the same documents on the website instead. Use the same account you use on the web or in Claude — the app is a client, not a separate space. The key is stored once and read by the app, the share extension, the widget, and the Siri intent through a shared Keychain group, so you sign in a single time. Signing out clears the key **and discards anything still waiting in the offline queue**, so that captures made under one account can never post under the next. ## The five tabs | Tab | What it does | |---|---| | **Capture** | Title, notes, a starting status, and tags with suggestions from your existing tag list | | **Search** | Semantic search across everything you have captured | | **Ideas** | The full list, filtered by status, with swipe actions and a context menu | | **Review** | Pending duplicates, new connections, and ideas that have gone quiet — badged with the count | | **Settings** | Account and plan, theme, offline queue, and links out to the web app | ### Capture The capture form offers `captured`, `exploring`, and `building` as starting statuses — the same three the web app's capture modal offers — and a tag field that suggests tags you already use. Ideas post with `source: ios`, shown as `⬢` everywhere else in NeuralRepo. Two conveniences worth knowing: - **Drafts survive.** Backgrounding the app with a part-written idea saves it; you come back to what you were typing. - **The form clears itself.** After a successful capture the fields reset, the keyboard returns to the title, and a "Captured" toast appears for about a second and a half. ### The offline queue If a capture cannot reach the server, the app keeps it locally and syncs it later. | Behaviour | Detail | |---|---| | What gets queued | Only requests that demonstrably never reached the server — a network failure, not a rejection | | Where | One file per capture in the App Group container, so the app and the share extension can queue at the same time | | Order | Oldest first, so capture order and idea numbering are preserved | | When it drains | On the next successful capture, when you open the app, or from **Sync now** on the banner | | When an item is deleted | Only after the server confirms the create | | What stops a drain | Being offline, or a `401` — everything stays queued | | Other errors | The item stays queued and the drain moves on, so a capture blocked by the free-plan cap retries after you upgrade | A banner on the Capture tab and a row in Settings both show the pending count. **Note:** Nothing is ever dropped, but nothing is deduplicated either — the API has no idempotency key, so the app deliberately only queues failures where the request certainly never arrived. ### The duplicate banner After a capture, the app polls for a duplicate detection three times over the next couple of seconds and, if the idea it just saved was flagged, raises a banner offering **View duplicate**, **Dismiss**, or a close button. Dismissing resolves the detection server-side so it will not come back. This is the one surface in NeuralRepo with a duplicate banner attached to capture, and it appears **after** the idea is saved — detection is asynchronous, so it cannot happen before. It is Pro-only, because duplicate detections are. ### Search, Ideas, and Review **Search** runs the same semantic search as `nrepo search`, 30 results at a time. When the server falls back to keyword results — on the free plan past 10 semantic searches a month — the list is labelled **Keyword results** and the match percentages disappear. **Ideas** filters by status with a chip bar, pages as you scroll, and supports swipe-to-archive, swipe-to-ship, a leading swipe to move to `building`, and a context menu for copying the title or setting any status. Opening an idea shows its body, tags, relations, and — at the bottom — `id 391 · #42`, which is the easiest way to read an idea's real ID off your phone for a CLI or API call. **Review** is the mobile version of the [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): possible duplicates (Merge / Not a duplicate), new connections (Open / Seen it), and ideas gone quiet past your stale threshold (Keep / Develop / Archive). The tab badge shows how many cards are waiting. ## Capturing without opening the app ### Share sheet Share a page from Safari, or selected text from anywhere, into NeuralRepo. The extension pulls the page title, text, and URL into a pre-filled capture form with the same status and tag controls as the app. It posts with `source: ios` and uses the same offline queue, so a share on the Underground still lands. ### Siri and Shortcuts The app registers a native App Intent, so these all work without opening it: - "Capture to NeuralRepo" - "Add an idea to NeuralRepo" - "New idea in NeuralRepo" Siri asks "What's the idea?", and the dictated text is saved with `source: siri`. Text longer than 200 characters is split: the first 200 become the title and the whole text becomes the body. If you are not signed in, the intent replies "Open the NeuralRepo app and sign in first"; at the free-plan cap it says so and points at Pro. This replaces the older iCloud shortcut described in [Siri Shortcuts](https://docs.neuralconfig.com/neuralrepo/capture/siri/) — the intent needs no download and no API key pasted into a shortcut. ### Widget A home and lock screen widget in small and medium sizes: a **New idea** button that deep-links straight into the capture screen, your active idea count, and — at medium size — your four most recent ideas. **Note:** The widget reads a snapshot the app writes, and asks iOS to refresh at most every 30 minutes. Its counts update when you open the app or capture something, not continuously. ## What the app does not do Organising work stays on the web: the mind map, the kanban board, the AI agent, tag management, bulk actions, and export are all web-app features. The iOS app is deliberately a capture-and-lookup client, and **Open web app** in Settings takes you to the rest. ## Next Steps - [Web App](https://docs.neuralconfig.com/neuralrepo/capture/web/): The full workspace the iOS app deliberately leaves out. - [Siri Shortcuts](https://docs.neuralconfig.com/neuralrepo/capture/siri/): The shortcut-based capture path, for devices without the app. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): What the Review tab is showing you, and what each action does. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): Which of the app's features — duplicates, unlimited search — need Pro. --- # Claude AI (MCP) Source: https://docs.neuralconfig.com/neuralrepo/capture/mcp/ NeuralRepo integrates with Claude AI through the **Model Context Protocol (MCP)**, allowing Claude to read, create, and manage your ideas directly during a conversation. No copy-pasting, no switching tabs. ## What Is MCP? The [Model Context Protocol](https://modelcontextprotocol.io) is an open standard that lets AI assistants interact with external tools and data sources. When NeuralRepo is connected as an MCP server, Claude gains access to a set of tools for managing your ideas — saving new ones, searching existing ones, linking ideas together, and more. ## Setup **Claude.ai** 1. Open **Claude.ai** and go to your account settings. 2. Navigate to the **Integrations** or **MCP Servers** section. 3. Click **Add Remote MCP Server** and enter the following URL: ``` https://neuralrepo.com/mcp/v2 ``` 4. Save the configuration. On your next conversation, Claude will have access to NeuralRepo tools. 5. The first time Claude attempts to use a NeuralRepo tool, you will be prompted to **authorize via OAuth**. Sign in with your NeuralRepo account to grant access. **Claude Code** Add the NeuralRepo MCP server from your terminal: ```bash claude mcp add --transport http neuralrepo https://neuralrepo.com/mcp/v2 ``` On the next Claude Code session, NeuralRepo tools will be available. The OAuth authorization flow triggers automatically on first use. **Note:** MCP works on **all plans** (Free and Pro). The `link_ideas` and `unlink_ideas` tools require a Pro plan. Semantic search is limited to 10/month on Free. The OAuth flow ensures Claude only accesses your ideas with your explicit permission. ## Available Tools **Tip:** The setup above uses `https://neuralrepo.com/mcp/v2`, which has 22 discrete tools (no `code_mode`); they're listed on [Claude Connector (MCP v2)](https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/). The table below is for the original `https://neuralrepo.com/mcp/` connector, which keeps working with no end date. Use it if you rely on `code_mode`. Connected to the original `/mcp/` address, Claude has access to the following tools: | Tool | Description | |------|-------------| | `save_idea` | Create a new idea with title, body, tags, status, source URL, or a parent idea | | `search_ideas` | Search your ideas semantically, or browse them by recency | | `get_idea` | Retrieve a specific idea by ID | | `update_idea` | Update an idea's title, body, status, or tags — one idea, or many at once | | `delete_idea` | Archive an idea by ID (a soft delete) | | `link_ideas` | Create typed relations between ideas — one pair, or a batch (Pro) | | `unlink_ideas` | Remove a relation between two ideas (Pro) | | `list_tags` | List all tags with their idea counts | | `code_mode` | Run JavaScript against **24** NeuralRepo functions in one call | `update_idea` and `link_ideas` each take a bulk form — an `ids` array and a `links` array respectively — so Claude can retag or connect a batch of ideas without a call per idea. `code_mode` is the one to reach for when a request needs several steps: it exposes a `neuralrepo` object with functions the eight named tools don't cover — merging ideas, managing tag colours, adding links, resolving duplicates, and repository stats. See the [MCP integration guide](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/). ## Usage Examples Once NeuralRepo is connected, you can interact with your ideas naturally in conversation: ### Saving an idea > **You:** I just thought of something — we should build a browser extension that highlights text and saves it as an idea. Save this to NeuralRepo. > > **Claude:** I have saved that as a new idea in NeuralRepo. *(calls `save_idea`)* ### Searching ideas > **You:** Do I have any ideas related to browser extensions? > > **Claude:** Let me check your ideas. *(calls `search_ideas`)* You have two related ideas: "Browser extension for bookmarking" and "Chrome plugin for code snippets." ### Linking ideas > **You:** Those two browser extension ideas are related. Link them. > > **Claude:** Done — I have linked them with a "related" relation. *(calls `link_ideas`)* ### Updating status > **You:** Move the habit tracker idea to "building" status. > > **Claude:** Updated. "Build a habit tracker app" is now in the `building` status. *(calls `update_idea`)* ## OAuth Authorization Flow NeuralRepo's MCP integration uses **OAuth 2.0** for secure authorization: 1. When Claude first attempts to use a NeuralRepo tool, you see an authorization prompt 2. Clicking **Authorize** opens the NeuralRepo OAuth consent screen 3. Sign in with your NeuralRepo account (GitHub, Google, Apple, or magic link) 4. Grant Claude permission to access your ideas 5. Authorization is persisted — you do not need to re-authorize for future sessions Two scopes exist: `ideas:read` and `ideas:write`. Anything that changes data — `save_idea`, `update_idea`, `delete_idea`, `link_ideas`, `unlink_ideas`, and `code_mode` — is refused without `ideas:write`. **Tip:** You can revoke MCP access at any time from **Settings > Connected Apps** in the NeuralRepo web dashboard. ## Limitations - MCP access is **read-write** — Claude can create, update, and archive ideas on your behalf. Review Claude's actions if you want to be cautious. - The `link_ideas` and `unlink_ideas` tools require a **Pro** plan; everything else works on Free. - Semantic search is limited to 10/month on Free, after which `search_ideas` returns keyword results with a notice. - On Free, `save_idea` refuses once you hold 50 active ideas. - Rate limits apply: Free allows 100 requests/day, Pro allows 10,000 requests/day, reset at midnight UTC. Every MCP call counts. ## Next Steps - [MCP Integration Guide](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): Advanced MCP configuration, the full code_mode surface, and troubleshooting. - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): How search_ideas resolves semantic versus keyword, and what the thresholds do. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): The relation types link_ideas can create, and the cycles it refuses. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): Which MCP tools need Pro, and what the daily request limits are. --- # Siri Shortcuts Source: https://docs.neuralconfig.com/neuralrepo/capture/siri/ NeuralRepo integrates with **Siri Shortcuts** for hands-free idea capture. Speak your idea, and it lands in your NeuralRepo inbox — no typing, no unlocking, no app switching. ## Quick Capture Once set up, capturing an idea is as simple as: > **"Hey Siri, NeuralRepo"** Siri prompts you for the idea title, sends it to NeuralRepo via the API, and confirms the capture. The idea is created with: - **Status**: `captured` - **Source**: `siri` - **Tags**: none (add them later from the web or CLI) ## Requirements - An active NeuralRepo **Pro** plan — the Shortcuts card in Settings is Pro-gated - An **API key**, generated from the same card - Apple device running iOS 16+, iPadOS 16+, macOS 13+, or watchOS 9+ ## Setup 1. **Open Settings ▸ Integrations** In the NeuralRepo web dashboard, scroll to the **Apple Shortcuts** card. On a free plan the card shows an upgrade link instead of the setup UI. 2. **Generate an API key** If you have no key yet, the card offers **Generate API Key** directly. Copy it — it starts with `nrp_` and is 68 characters long. **Caution:** Store your API key securely. It grants full access to your NeuralRepo account and cannot be viewed again after generation. If you lose it, generate a new one. 3. **Add the Quick Capture shortcut** Click **Add to Shortcuts** on the card. It opens the NeuralRepo Quick Capture shortcut in Apple's Shortcuts app — an iCloud shortcut, so it syncs to every device signed in to the same Apple account. 4. **Configure the API key** When you first run the shortcut, it prompts you to paste your API key. This is stored securely in the Shortcuts app and used for all subsequent captures. 5. **Test it** Say **"Hey Siri, NeuralRepo"** or tap the shortcut in the Shortcuts app. Speak or type your idea when prompted. You should see a confirmation that the idea was saved. **Tip:** There is one shortcut — Quick Capture. Expand **"What can I say to Siri?"** on the same card for the phrase list and the places you can put the shortcut: Home Screen, a widget, or the Action Button. ## Supported Devices **iPhone & iPad** Full voice capture via Siri. Works from the lock screen, home screen, or while using other apps. **Mac** Use Siri from the menu bar or keyboard shortcut. Ideal for capturing ideas while working at your desk. **Apple Watch** Raise your wrist and say "Hey Siri, NeuralRepo" for the fastest possible capture. Dictation handles the transcription. ## Tips - **Keep titles short and clear** — Siri's dictation works best with concise phrases. You can always add detail later. - **Use a distinctive shortcut name** — if "NeuralRepo" is hard for Siri to recognize, rename the shortcut to something like "Save Idea" or "Idea Capture." - **Add to your Home Screen** — add the shortcut as a home screen icon for one-tap capture without voice. **Tip:** Ideas captured via Siri land in your inbox with `captured` status. Triage them on the web or CLI later — add tags, write a body, and move them to `exploring`. ## Next Steps - [Siri Shortcuts Setup Guide](https://docs.neuralconfig.com/neuralrepo/integrations/siri-shortcuts/): Step-by-step instructions for creating custom shortcuts, adding parameters, and troubleshooting. - [Apple Watch](https://docs.neuralconfig.com/neuralrepo/integrations/apple-watch/): Capture from the wrist, and what the watch can and cannot do. - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): Dictated ideas arrive untagged — here is what happens next. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): Triage the pile of quick captures without reading the whole list. --- # Web App Source: https://docs.neuralconfig.com/neuralrepo/capture/web/ The web dashboard at [neuralrepo.com](https://neuralrepo.com) is NeuralRepo's primary interface. It provides the full feature set — idea creation, kanban workflow, search, mind map visualization, and settings management. ## Dashboard Overview The left sidebar is the dashboard's spine: a **+ Capture** button at the top, then the six views, with **Settings** at the bottom. | View | What it is | |------|-----------| | **Ideas** | The filterable list of everything you have captured | | **Board** | The kanban board — one column per status, drag to move | | **Mind Map** | Your idea graph as an interactive force-directed map | | **Agent** | The AI agent: a chat panel beside an artifact canvas (Pro) | | **Duplicates** | Pending duplicate detections to merge or dismiss (Pro) | | **Review** | The resurfacing queue — quiet ideas, new connections, duplicates | Each view has its own URL (`/app`, `/board`, `/map`, `/agent`, `/duplicates`, `/review`), so you can bookmark or link to one directly. From the **Ideas** list you can: - **Filter by status** — narrow to `captured`, `exploring`, `building`, `shipped`, or `shelved` - **Filter by tags** — click any tag in the sidebar; click more to narrow further - **Filter by source** — see ideas from a specific capture channel - **Search** — the sidebar search box runs keyword or semantic search - **Select and act in bulk** — tick several ideas and use the bulk toolbar ## Creating Ideas 1. Click **+ Capture** at the top of the sidebar (it is on every view). 2. Fill in the idea details: | Field | Required | Description | |-------|:--------:|-------------| | **Title** | Yes | A concise name for the idea | | **Notes** | No | Longer description — supports full Markdown | | **Status** | No | `captured` (default), `exploring`, or `building` | | **Tags** | No | Type a name and press Enter, or click one of your existing tags | 3. Click **Capture**. **Tip:** The notes field supports full Markdown including headings, lists, code blocks, and links. Use it to capture context, requirements, or early notes. The capture form only offers the first three statuses — an idea you are capturing has not shipped or been shelved yet. Set either from the idea's detail panel or the board afterwards. If you leave the tag field empty, NeuralRepo tags the idea for you a few seconds later. See [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/#auto-classification). ## Duplicates Surface After the Save, Not Before Duplicate detection is asynchronous — the idea is saved first, then embedded and compared in the background. There is no warning banner in the capture form, and nothing blocks a save. A few seconds later, any pair above your dedup threshold appears in the **Duplicates** view (a Pro feature) and in the duplicates section of **Review**, where you merge or dismiss it. On the free plan the detections are still recorded — they are simply not displayed, and they are all there waiting if you upgrade later. See [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/). ## Bulk Actions Tick the checkbox on any idea in the list and a bulk toolbar appears. With a selection you can: - **Move** every selected idea to a status - **Add** or **remove** a tag across the selection - **Archive** the selection (after a confirmation) Bulk edits are applied **per idea, not as a transaction** — the API takes up to 50 ideas per request (the web app splits larger selections into batches), and reports success or failure for each one. A bad ID fails alone; the rest still change. ## Kanban Board Open the **Board** view from the sidebar. The kanban board displays ideas as cards in columns: | Column | Status | |--------|--------| | Captured | New, unevaluated ideas | | Exploring | Ideas under research or consideration | | Building | Ideas actively being implemented | | Shipped | Completed and delivered ideas | | Shelved | Parked ideas — not active, not deleted | **Drag and drop** cards between columns to change their status. This is the fastest way to move ideas through your workflow. **Note:** The kanban board respects your current tag and source filters. If you have a tag filter active, only matching ideas appear on the board. ## Search The sidebar search box supports two modes, and both are available on every plan: **Semantic** Vector-powered search that finds ideas by **meaning**. A search for "daily routine app" will match ideas titled "habit tracker" or "morning checklist tool" even if the exact words do not appear. ``` app for tracking daily routines ``` The free plan includes 10 semantic searches a month. After that, searches return keyword results with a notice above them until the month rolls over. **Keyword** Full-text matching across idea titles and bodies, with three matching styles — **any word**, **all words**, or **exact** phrase. Unlimited on every plan. ``` habit tracker mobile ``` Results are ranked by relevance and can be further narrowed by status, tags, and source. The result header tells you which mode answered — `semantic`, or `keyword` with the matching style in brackets. See [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/) for thresholds and the API. ## Mind Map The **Mind Map** view renders your idea graph as an interactive network visualization. Each idea is a node, and relations are edges connecting them. Free accounts get it **view-only** — pan, zoom, filter, and open ideas; editing the graph requires Pro. - **Zoom and pan** to navigate the graph - **Click a node** to open the idea detail view - **Color coding** reflects idea status - **Edge labels** show the relation type (related, blocks, inspires, etc.) The mind map is especially useful for spotting clusters of related ideas, identifying blocking dependencies, and understanding how your ideas connect across projects. ## Settings **Settings** sits at the bottom of the sidebar. From here you can: - **Generate API keys** for the CLI, Shortcuts, and the REST API (Pro) - **Manage billing** — upgrade, downgrade, or cancel your plan - **Configure BYOK AI** — add your own Anthropic, OpenAI, or OpenRouter key (Pro) - **Set up integrations** — the Claude MCP connector, the CLI, and the Siri shortcut - **Tune notifications** — the weekly digest, stale-idea nudges, and the staleness threshold - **Export data** — download every idea as JSON, CSV, or a Markdown archive - **Manage account** — display name, theme, and connected sign-in providers **Tip:** The Markdown export is Obsidian-ready: one note per idea with YAML frontmatter, and relations written as `[[wiki-links]]` so the vault reproduces your idea graph. ## Next Steps - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): Work the resurfacing queue — quiet ideas, new connections, and possible duplicates. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): Read the graph: node anatomy, edge types, filters, 3D, and the agent panel. - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): Semantic versus keyword, thresholds, and what each surface falls back to. - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): Move ideas through captured, exploring, building, shipped, and shelved. --- # nrepo login / logout / whoami Source: https://docs.neuralconfig.com/neuralrepo/cli/auth-commands/ ## nrepo login ```bash nrepo login [--api-key] ``` Authenticates the CLI and writes an `nrp_` key to `~/.config/neuralrepo/config.json`. Without flags it runs the browser flow through GitHub; with `--api-key` it prompts for a key you already have. [Authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/) covers both flows end to end. | Flag | Type | Default | Description | |---|---|---|---| | `--api-key` | boolean | `false` | Prompt for an API key instead of opening the browser. | ```bash # Browser login (any plan) nrepo login # API key login — prompts, does not take the key as an argument nrepo login --api-key # Same thing, non-interactive echo "$NEURALREPO_API_KEY" | nrepo login --api-key ``` Both paths end with the same line, which includes your plan: ``` ✔ Logged in as Alice (pro) ``` **Note:** Logging in again simply overwrites the stored key. The previous key stays valid on the server — browser login mints a new one every time, so repeated logins accumulate keys labelled `CLI (auto-generated)` in `nrepo key list`. ## nrepo logout ```bash nrepo logout # Logged out. ``` Overwrites the local config file with `{}`. Every other command then fails with `Not logged in. Run \`nrepo login\` to authenticate.` until you log in again. **Caution:** Logging out is local only — the key is not revoked and keeps working anywhere else it is stored. Use [`nrepo key revoke`](https://docs.neuralconfig.com/neuralrepo/cli/key/) when a key may have leaked. ## nrepo whoami ```bash nrepo whoami [--json|--human] ``` Fetches the current account from `GET /user/me`. It is the cheapest way to check whether the CLI is authenticated at all. | Flag | Type | Default | Description | |---|---|---|---| | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ```bash nrepo whoami ``` ``` Alice Email: alice@example.com Plan: pro ID: usr_abc123 ``` The plan prints as the raw value — `free` or `pro`. Trial accounts report `pro`. ```bash nrepo whoami --json ``` ```json { "id": "usr_abc123", "email": "alice@example.com", "display_name": "Alice", "avatar_url": "https://avatars.githubusercontent.com/u/1?v=4", "plan": "pro", "settings_json": "{}", "has_anthropic_key": true, "has_openai_key": false, "has_openrouter_key": false, "active_idea_count": 137, "is_trialing": false, "trial_days_remaining": null, "created_at": "2026-01-04 09:12:33", "updated_at": "2026-07-21 18:40:02" } ``` `active_idea_count` counts unarchived ideas, so it is the field to watch against the free plan's 50-idea cap: ```bash nrepo whoami --json | jq 'select(.plan == "free") | .active_idea_count' ``` `is_trialing` is true only while a Pro trial is running with no Stripe subscription behind it; `trial_days_remaining` is `null` otherwise. ## Next Steps - [Authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/): Which identity provider the browser flow uses, and how to authenticate without one. - [nrepo key](https://docs.neuralconfig.com/neuralrepo/cli/key/): List, create, and revoke the keys these commands authenticate with. - [nrepo status](https://docs.neuralconfig.com/neuralrepo/cli/status/): The same account summary plus a breakdown of every idea by status. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): What the free plan caps and what Pro unlocks across every surface. --- # Authentication Source: https://docs.neuralconfig.com/neuralrepo/cli/authentication/ Both login methods end in the same place: an `nrp_` API key in `~/.config/neuralrepo/config.json`, sent as an `X-API-Key` header on every request. | Method | Best for | Command | Plan required | |---|---|---|---| | **Browser** | Interactive use on a workstation | `nrepo login` | Any — including free | | **API key** | CI, scripts, headless machines | `nrepo login --api-key` | Pro, to create the key | ## Browser login ```bash nrepo login ``` 1. The CLI starts a one-shot HTTP server on a **random local port** between 49152 and 65535 and prints the sign-in URL: ``` Starting local auth server... Open this URL to log in: https://neuralrepo.com/auth/cli?callback=http%3A%2F%2Flocalhost%3A51234%2Fcallback Waiting for authentication... ``` 2. It also tries to open that URL for you (`open` on macOS, `start` on Windows, `xdg-open` elsewhere). If nothing opens, paste the printed URL yourself. 3. You sign in with **GitHub**. NeuralRepo matches the account by your GitHub **verified primary email**, so this works whether or not you originally signed up through GitHub, as long as that email is the one your account uses. If it isn't, the login creates a **separate new account**. 4. The server redirects back to the local callback with a freshly minted key. The CLI saves it and prints: ``` ✔ Logged in as Alice (pro) ``` ```mermaid sequenceDiagram participant C as nrepo participant L as localhost:PORT participant B as Your browser participant N as neuralrepo.com participant G as GitHub C->>L: Start one-shot server on a random port C->>B: Open /auth/cli?callback=http://localhost:PORT/callback B->>N: GET /auth/cli N->>B: Redirect to /auth/github with the callback stored B->>G: Sign in and approve G->>N: OAuth code N->>N: Match account by verified primary email N->>N: Mint an nrp_ key labelled "CLI (auto-generated)" N->>B: Redirect to localhost:PORT/callback?api_key=nrp_... B->>L: GET /callback with the key L->>C: Hand over the key, close the server C->>N: GET /user/me to verify C->>C: Write ~/.config/neuralrepo/config.json ``` **Caution:** The local server gives up after **120 seconds**. If the browser sign-in takes longer than that, the CLI fails with `Login timed out after 120 seconds` — re-run `nrepo login` rather than finishing the browser flow, because the callback has nowhere left to land. Two consequences of that flow are worth knowing: - **`nrepo login` mints a real API key**, not a session token. It is labelled `CLI (auto-generated)` and appears in `nrepo key list` alongside keys you made yourself. - **No plan check runs on that path.** Browser login works on the free plan even though creating a key from the dashboard or with `nrepo key create` is a Pro feature. `nrepo login` always authenticates through GitHub — it sends no provider preference, and the endpoint's default is GitHub. Google sign-in is not part of this flow at all. ## API key login `--api-key` is a bare flag. It prompts; it does not take the key as an argument. **Interactive** ```bash nrepo login --api-key # Enter your API key: nrp_... # ✔ Logged in as Alice (pro) ``` **Piped** ```bash echo "$NEURALREPO_API_KEY" | nrepo login --api-key ``` The prompt reads a single line from stdin, so piping and here-strings both work. The key is verified against `GET /user/me` before it is saved. A bad key fails with `Invalid API key. Generate one at https://neuralrepo.com/settings` and exit status 1. Keys use the format `nrp_` followed by 64 hexadecimal characters. Create one with [`nrepo key create`](https://docs.neuralconfig.com/neuralrepo/cli/key/) or in **Settings ▸ API Keys** on the web dashboard; both require Pro. **Tip:** Signed in with Google or a magic link, on the free plan? Browser login still works — GitHub matches you by verified primary email, so your GitHub primary email must be the one you signed up with; otherwise you land in a separate new account. If you have no GitHub account at all, you need Pro to mint a key, because that is the only other way in. ## Configuration file ``` ~/.config/neuralrepo/config.json ``` | Field | Description | |---|---| | `api_url` | API base URL. Defaults to `https://neuralrepo.com/api/v1` when absent | | `api_key` | The `nrp_` key, in plain text | | `user_id` | Your account identifier, recorded at login | | `auth_method` | `browser` or `api-key` — recorded at login and **never read again** | **Caution:** The file is plain text with no special permissions. Treat it like an SSH private key: never commit it, never bake it into an image. ## Output format Every command prints human-readable output by default, on **both** login methods. Pass `--json` for machine-parseable output, and `--human` to force text back on if something upstream in your tooling sets `--json`. `--human` wins when both are given. **Caution:** `nrepo login --api-key` finishes by printing "Output defaults to JSON. Use `--human` for human-readable output." That line is wrong, and has been since the flag was added: `auth_method` is written to the config file and never read, so output is human-readable until you pass `--json`. Script accordingly. ## Headless and CI ```bash export NREPO_NO_UPDATE_CHECK=1 echo "$NEURALREPO_API_KEY" | nrepo login --api-key nrepo push "Nightly build failed on $(git rev-parse --short HEAD)" \ --body "$(tail -50 build.log)" --tag ci --json ``` Give CI its own key so you can revoke it independently, and check the exit status: every failure exits 1, and with `--json` the error is a single JSON object on stderr. ## Logging out ```bash nrepo logout # Logged out. ``` **Caution:** `nrepo logout` overwrites the local config file with `{}`. It does **not** revoke the key on the server — anyone holding a copy can still use it. To invalidate it, run [`nrepo key revoke`](https://docs.neuralconfig.com/neuralrepo/cli/key/) first, or delete the key in the web dashboard. ## Next Steps - [nrepo login / logout / whoami](https://docs.neuralconfig.com/neuralrepo/cli/auth-commands/): The full flag and output reference for the three authentication commands. - [nrepo key](https://docs.neuralconfig.com/neuralrepo/cli/key/): Create, list, and revoke the API keys the CLI authenticates with. - [Troubleshooting](https://docs.neuralconfig.com/neuralrepo/cli/troubleshooting/): What an expired session, a Pro gate, and a network failure look like on the terminal. - [API authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): The same key, used directly against the REST API from any language. --- # nrepo branch Source: https://docs.neuralconfig.com/neuralrepo/cli/branch/ ```bash nrepo branch [options] ``` Creates a **new** idea from an existing one, recording the original as its parent. Use it to explore an alternative without editing — or losing — the version you already have. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--title ` | string | The original's title | Title for the variant. | | `--body <body>` | string | The original's body | Body for the variant. | | `--json` | boolean | `false` | Output the new idea as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## What the new idea inherits | Field | Value on the branch | |---|---| | Title | The original's, unless `--title` is given | | Body | The original's, unless `--body` is given | | Tags | Copied from the original | | Status | Always `captured` — never the original's status | | Source | `cli`, whatever the original's source was | | Parent | The original, recorded in `parent_id` | Nothing is copied back the other way, and the original is not modified. ## Output ```bash nrepo branch 391 --title "Dark mode — Tailwind approach" ``` ``` ✓ Branched from #391 as #55 #55 captured ⬡ Dark mode — Tailwind approach [ui, frontend] Processing: embeddings, dedup, and auto-tagging queued Compare with: nrepo diff 391 508 ``` **Note:** The first line mixes two kinds of number: `#391` is the **ID you passed**, `#55` is the new idea's display number. The `nrepo diff` hint below it uses IDs for both, so it is safe to copy and run as printed. ## The parent relation is created a moment later Setting `parent_id` is synchronous, but the visible `parent` relation between the two ideas is created by the background queue that also handles embedding and dedup. For a second or two after branching, `nrepo links` on the new idea shows nothing: ```bash nrepo branch 391 --json | jq -r .id # 508 nrepo links 508 # "No links" — the queue has not run yet sleep 3 nrepo links 508 # Child of ← #42 ``` The relation is recorded as system-created, so it appears in the mind map styled differently from relations you draw by hand. [`nrepo diff 508`](https://docs.neuralconfig.com/neuralrepo/cli/diff/) works immediately either way, because it reads `parent_id` directly. ## Next Steps - [nrepo diff](https://docs.neuralconfig.com/neuralrepo/cli/diff/): Compare the variant against the idea it came from. - [nrepo merge](https://docs.neuralconfig.com/neuralrepo/cli/merge/): Fold the winning variant back into the original. - [nrepo link](https://docs.neuralconfig.com/neuralrepo/cli/link/): Add relations by hand when parent is not the right shape. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): What parent means alongside the other relation types. --- # nrepo diff Source: https://docs.neuralconfig.com/neuralrepo/cli/diff/ ```bash nrepo diff <id> [id2] [options] ``` Compares two ideas across five fields — title, body, status, source, and tags — and prints only the ones that differ. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--json` | boolean | `false` | Output both ideas plus a field-by-field diff as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Output ```bash nrepo diff 391 402 ``` ``` diff #42 → #43 ──────────────────────────────────────────────────────────── Title - Add dark mode to dashboard + Add dark mode and a theme picker Body Switch the dashboard to support both schemes. - Use CSS custom properties. + Use CSS custom properties and a picker in Settings. Tags - ui, frontend + ui, frontend, themes ``` Identical ideas print `No differences found.` An empty field renders as `(empty)`. **Note:** The body comparison is line-by-line **by position**, not a real diff. Insert a line near the top of one body and every line after it is reported as changed, because line 5 is now being compared against line 4. It is a readable summary, not a patch. ## Comparing against a related idea With one ID, the CLI picks the comparison target itself, in this order: 1. The idea's `parent_id` — set when it was created by [`nrepo branch`](https://docs.neuralconfig.com/neuralrepo/cli/branch/). 2. A `parent` relation. 3. A `duplicate` relation. 4. The highest-scoring relation of any type. ```bash nrepo diff 402 # Compares #43 against the idea it was branched from ``` An idea with none of those has nothing to compare against: ``` No parent or related idea to diff against. Usage: nrepo diff 402 <other-id> ``` That case exits 0 in human mode. With `--json` it exits 1 and prints `{"error":"...","code":"no_diff_target"}`. ## JSON output ```bash nrepo diff 391 402 --json ``` ```json { "a": { "id": 391, "number": 42, "title": "Add dark mode to dashboard" }, "b": { "id": 402, "number": 43, "title": "Add dark mode and a theme picker" }, "diff": [ { "field": "Title", "a": "Add dark mode to dashboard", "b": "Add dark mode and a theme picker", "changed": true }, { "field": "Body", "a": "...", "b": "...", "changed": true }, { "field": "Status", "a": "exploring", "b": "exploring", "changed": false }, { "field": "Source", "a": "cli", "b": "cli", "changed": false }, { "field": "Tags", "a": "ui, frontend", "b": "ui, frontend, themes", "changed": true } ] } ``` `a` and `b` are the full idea objects; `diff` carries every field, including unchanged ones. ## Next Steps - [nrepo branch](https://docs.neuralconfig.com/neuralrepo/cli/branch/): Create the variant that nrepo diff compares against its parent. - [nrepo merge](https://docs.neuralconfig.com/neuralrepo/cli/merge/): Fold one of the two ideas into the other once you have decided. - [nrepo duplicate](https://docs.neuralconfig.com/neuralrepo/cli/duplicate/): Review pairs the system flagged as near-identical. - [nrepo show](https://docs.neuralconfig.com/neuralrepo/cli/show/): Read either idea in full instead of a field summary. --- # nrepo duplicate Source: https://docs.neuralconfig.com/neuralrepo/cli/duplicate/ ```bash nrepo duplicate list [options] nrepo duplicate dismiss <id> [options] nrepo duplicate merge <id> [options] ``` NeuralRepo compares every new idea against your existing ones a few seconds after capture and records a **detection** for each near-identical pair. These three subcommands work through that queue. | Subcommand | What it does | |---|---| | `list` | Show unresolved detections | | `dismiss <id>` | Mark a detection resolved without changing either idea | | `merge <id>` | Fold the pair together and resolve the detection | All three take `--json` and `--human`. **Caution:** **Duplicate detection is a Pro feature.** On the free plan the endpoint returns an empty list, so `nrepo duplicate list` always prints `No pending duplicates.` Detections are still being recorded in the background, and the backlog appears in full the moment you upgrade. ## Listing detections ```bash nrepo duplicate list ``` ``` 2 pending duplicates #101 Auth flow redesign ≈ #42 Rewrite auth flow 91% #103 Dark mode support ≈ #40 Add dark mode to dashboard 85% Use `nrepo duplicate dismiss <id>` or `nrepo duplicate merge <id>` to resolve. ``` **Caution:** The `<id>` those two commands want is the **detection's** ID, and the human output does not print it — `#101` and `#42` are idea display numbers. Use `--json` to get it: ```bash nrepo duplicate list --json | jq -r '.duplicates[] | "\(.id) #\(.idea_number) ≈ #\(.duplicate_number) \(.similarity_score)"' ``` The JSON form carries both ideas in full — numbers, titles, statuses, sources, creation dates, and the first 280 characters of each body — which is enough to decide without a second lookup: ```json { "duplicates": [ { "id": 17, "idea_id": 512, "duplicate_of_id": 391, "similarity_score": 0.91, "status": "pending", "idea_number": 101, "idea_title": "Auth flow redesign", "duplicate_number": 42, "duplicate_title": "Rewrite auth flow" } ] } ``` The human list shows only `pending` detections; `--json` returns what the API sent, which is also pending-only. ## Dismissing ```bash nrepo duplicate dismiss 17 # ✓ Dismissed duplicate #17 ``` Marks the detection resolved. Neither idea changes, and the pair is not flagged again. A detection that does not exist, or has already been resolved, returns `API error (404): Duplicate not found or already resolved`. ## Merging ```bash nrepo duplicate merge 17 # ✓ Merged duplicate #17 into primary idea ``` **Caution:** This is **not** the same operation as [`nrepo merge`](https://docs.neuralconfig.com/neuralrepo/cli/merge/), and the difference decides which idea survives. | | `nrepo duplicate merge` | `nrepo merge <keep> <absorb>` | |---|---|---| | Idea kept | The **newer** one — the capture that triggered the detection | Whichever you name first | | Idea archived | The older one it matched | The one you name second | | Body | Newer body, `---`, older body | Kept body, `---`, absorbed body | | Tags | Union | Union | | Relations | **Not transferred** — they stay on the archived idea and become invisible | Transferred to the kept idea | | Status of archived idea | Unchanged, but archived | Set to `shelved`, and archived | If the older idea is the one carrying your relations, merge it explicitly with `nrepo merge` instead, then dismiss the detection. Both paths keep the earlier of the two creation dates on the surviving idea, and neither can be undone. ## Next Steps - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): What the detector compares, the score threshold, and what it does with the pair. - [nrepo merge](https://docs.neuralconfig.com/neuralrepo/cli/merge/): Merge two ideas you chose yourself, with relation transfer. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): The web app's version of this queue, with connections and stale ideas too. - [nrepo diff](https://docs.neuralconfig.com/neuralrepo/cli/diff/): Compare the two ideas in a detection before you resolve it. --- # nrepo edit Source: https://docs.neuralconfig.com/neuralrepo/cli/edit/ ```bash nrepo edit <id> [options] ``` Updates the title, the body, or both. Anything you do not pass is left alone. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--title <title>` | string | _(unchanged)_ | New title. Max 200 characters. | | `--body <body>` | string | _(unchanged)_ | New body. Max 50,000 characters. | | `--json` | boolean | `false` | Output the updated idea as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | `nrepo edit` only touches those two fields. Status is [`nrepo move`](https://docs.neuralconfig.com/neuralrepo/cli/move/), tags are [`nrepo tag`](https://docs.neuralconfig.com/neuralrepo/cli/tag/). ## Examples ```bash # Retitle nrepo edit 391 --title "Add dark mode and a theme picker" # Replace the body from a file nrepo edit 391 --body "$(cat notes.md)" # Both at once nrepo edit 391 \ --title "Theme system v2" \ --body "Complete redesign of the theming layer with dark mode support." ``` **Caution:** `--body` **replaces** the body; it does not append. To add to what is there, read it first: `nrepo edit 391 --body "$(nrepo show 391 --json | jq -r .body)$(printf '\n\n')New paragraph."` Passing neither flag is an error, and nothing is sent: ``` Nothing to update. Use --title or --body. ``` With `--json` the same case prints `{"error":"Nothing to update. Use --title or --body.","code":"no_input"}`. Both exit with status 1. ## Output ``` ✓ Updated #42 Title: Theme system v2 Body updated (312 chars) ``` Only the fields you changed are echoed. `--json` prints the full updated idea object. ## What happens afterwards Editing a title or body re-queues the idea for embedding, so its semantic search results and duplicate detections refresh a few seconds later. Until that finishes, `nrepo search` still matches the old wording. ## Next Steps - [nrepo show](https://docs.neuralconfig.com/neuralrepo/cli/show/): Read the current title and body before you overwrite them. - [nrepo branch](https://docs.neuralconfig.com/neuralrepo/cli/branch/): Keep the original and explore a variant instead of editing in place. - [nrepo move](https://docs.neuralconfig.com/neuralrepo/cli/move/): Change status — the field nrepo edit deliberately does not touch. - [Queue Processing](https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/): What the background pass does with an idea after you change its text. --- # nrepo graph Source: https://docs.neuralconfig.com/neuralrepo/cli/graph/ ```bash nrepo graph <id> [options] ``` Walks outward from one idea through its relations and prints what it finds as a tree. Relations are followed in **both** directions, so an idea that only ever appears as a target still shows up. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--depth <n>` | number | `1` | Hops to traverse. Clamped to a maximum of `5`. | | `--type <types>` | string | _(all)_ | Comma-separated relation types to follow: `related`, `blocks`, `inspires`, `supersedes`, `parent`, `duplicate`. | | `--json` | boolean | `false` | Output nodes and edges as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Output ```bash nrepo graph 391 --depth 2 ``` ``` #42 Rewrite auth flow [exploring] ├── blocks → #43 Add dark mode and a theme picker [exploring] │ └── related → #61 Design tokens audit [captured] └── related → #18 Session cookie cleanup [captured] ``` Each branch is labelled with the relation type that produced it. An idea with no relations prints just itself: ``` #42 Rewrite auth flow [exploring] No connections found ``` **Caution:** The tree renders **three levels** — the root, its neighbours, and their neighbours. `--depth 3`, `4`, and `5` do fetch the deeper nodes, and they appear in `--json`, but the text tree stops drawing at the second hop. Use `--json` when you ask for more than two. **Note:** Traversal makes one API call per node visited, so a wide graph at depth 3 is a lot of round trips. Narrow it with `--type` when you only care about one kind of edge. ## Filtering by type ```bash # Just the dependency chain nrepo graph 391 --depth 3 --type blocks --json # Structural relations only nrepo graph 391 --type parent,supersedes ``` `--type` matches exact type names. An unknown name is not an error — it simply matches nothing, and you get the root alone. ## JSON output ```bash nrepo graph 391 --depth 2 --json ``` ```json { "root": 391, "depth": 2, "types": null, "nodes": [ { "id": 391, "number": 42, "title": "Rewrite auth flow", "status": "exploring", "depth": 0 }, { "id": 402, "number": 43, "title": "Add dark mode and a theme picker", "status": "exploring", "depth": 1 } ], "edges": [ { "source": 391, "target": 402, "type": "blocks" } ] } ``` `depth` echoes the clamped value you asked for, not how far the walk actually reached. `types` is `null` when `--type` was not passed. Edges keep their original direction regardless of which way the traversal crossed them, so the output can be fed straight into a graph layout tool. Each idea appears once: the first time the walk reaches it. In a diamond-shaped graph only the first edge into a node is drawn in the tree, though every edge is present in `edges`. ## Next Steps - [nrepo links](https://docs.neuralconfig.com/neuralrepo/cli/link/): One idea's relations in full, grouped by direction and type. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): The same graph rendered visually, with filters and time windows. - [The Idea Graph](https://docs.neuralconfig.com/neuralrepo/guides/idea-graph/): How connections accumulate and what to do with the clusters they form. - [nrepo link](https://docs.neuralconfig.com/neuralrepo/cli/link/): Add the relations this command traverses. --- # Installation Source: https://docs.neuralconfig.com/neuralrepo/cli/installation/ The CLI ships as a single npm package, `@neuralconfig/nrepo`, and installs one binary: `nrepo`. ## Requirements - **Node.js 18** or later (the package declares `engines.node` as `>=18`) - npm (included with Node.js) ## Install 1. Install the package globally: ```bash npm install -g @neuralconfig/nrepo ``` 2. Confirm the binary is on your `PATH`: ```bash nrepo --version # 0.0.9 ``` 3. Authenticate: ```bash nrepo login ``` Until you have logged in once, `nrepo --help` ends with a short getting-started box. It disappears as soon as `~/.config/neuralrepo/config.json` exists — the CLI prints it there because npm suppresses the package's own post-install message on most systems. ## Update ```bash npm install -g @neuralconfig/nrepo@latest ``` `npm update -g @neuralconfig/nrepo` also works. Nothing in the CLI updates itself — the version check below only tells you that an update exists. ## The version check Every invocation runs a non-blocking version check: | Behaviour | Detail | |---|---| | When it runs | Before the command executes, so the notice prints **above** the command's own output | | Where it prints | **stderr**, so it never contaminates `--json` output on stdout | | How often it calls npm | At most **once a week**, cached in `~/.config/neuralrepo/update-check.json` | | Network timeout | 5 seconds, in the background; the command never waits for it | | On failure | Silent — a failed check never fails the command | | Disable it | Set `NREPO_NO_UPDATE_CHECK=1` | The notice looks like this: ``` nrepo 0.1.0 available (current: 0.0.9). Run npm i -g @neuralconfig/nrepo to update. ``` **Tip:** Because the notice goes to stderr, `nrepo log --json | jq .` is safe as-is. Set `NREPO_NO_UPDATE_CHECK=1` in CI anyway, to avoid a weekly outbound call to the npm registry. ## The bundled Claude Code skill The package bundles a Claude Code skill. It is **not** installed by `npm install` — you opt in: ```bash nrepo install-skill # Claude Code skill installed. # /Users/you/.claude/skills/neuralrepo/SKILL.md ``` If `~/.claude` does not exist, the command reports that Claude Code is not installed and exits with status 1 without writing anything. Two things then happen on their own: - When the weekly version check finds a **newer** release, it refreshes that `SKILL.md` in the background, so the skill stays in step with the CLI between upgrades. - `npm uninstall -g @neuralconfig/nrepo` deletes `~/.claude/skills/neuralrepo/` entirely. ## Troubleshooting If `nrepo` is not found after installing, your npm global bin directory is probably not on your `PATH`: ```bash npm config get prefix # Add <prefix>/bin to your PATH export PATH="$(npm config get prefix)/bin:$PATH" ``` ## Next Steps - [Authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/): What nrepo login actually does, which identity provider it uses, and how to authenticate on a headless machine. - [nrepo push](https://docs.neuralconfig.com/neuralrepo/cli/push/): Create your first idea from the terminal, with tags and an initial status. - [Troubleshooting](https://docs.neuralconfig.com/neuralrepo/cli/troubleshooting/): Which argument is the idea ID, what the error output means, and which commands need Pro. - [Claude Code](https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/): What the bundled skill teaches Claude Code, and the workflow it enables. --- # nrepo key Source: https://docs.neuralconfig.com/neuralrepo/cli/key/ ```bash nrepo key list [options] nrepo key create <label> [options] nrepo key revoke <key-id> [options] ``` API keys authenticate the CLI, Siri Shortcuts, and any direct call to the REST API. They look like `nrp_` followed by 64 hexadecimal characters, and only the hash is stored server-side. All three subcommands take `--json` and `--human`. **Caution:** **`nrepo key create` requires Pro.** On the free plan it fails with `This feature requires a Pro plan. Upgrade at https://neuralrepo.com/settings`. Listing and revoking work on any plan — and so does `nrepo login`, which mints a key through a path with no plan check, so free accounts can still use the CLI. ## List ```bash nrepo key list ``` ``` 2 API keys ci-pipeline ak_9f2c1b7e Created: Jan 15, 2026 Last used: Jul 23, 2026 CLI (auto-generated) ak_44ad0c31 Created: Feb 20, 2026 Last used: never ``` The second value on each line is the **key ID** — what `revoke` takes. The key itself is never shown again after creation. A key that has never been used prints `never` rather than a date. With no keys: ``` No API keys found. Create one with `nrepo key create <label>`. ``` `--json` returns `{ "api_keys": [ { "id", "label", "created_at", "last_used_at" } ] }`. **Note:** Keys labelled `CLI (auto-generated)` were minted by `nrepo login`. Every browser login creates another one, so a machine you have logged in on repeatedly accumulates them. Revoking the extras is safe as long as you keep the one your current session is using. ## Create ```bash nrepo key create "github-actions" ``` ``` ✓ API key created Label: github-actions Key: nrp_4f8a2b... Save this key now — it won't be shown again. ``` **Danger:** The full key is displayed exactly once. There is no way to retrieve it later — a lost key has to be revoked and replaced. Labels are capped at 100 characters. For scripting, take the key straight from `--json`: ```bash KEY=$(nrepo key create "ci-$(date +%Y%m)" --json | jq -r '.key') ``` ## Revoke ```bash nrepo key revoke ak_9f2c1b7e # ✓ API key ak_9f2c1b7e revoked. ``` Takes the **key ID** from `nrepo key list`, not the label and not the key itself. Revocation is immediate and permanent; a key ID that does not exist returns `API error (404): API key not found`. **Caution:** Revoking the key your own session is signed in with logs this machine out — every subsequent command fails with `Authentication expired. Run \`nrepo login\` to re-authenticate.` Check `nrepo whoami` against the label before revoking, and note that `nrepo logout` does the reverse: it clears the local file but leaves the key valid. ## Next Steps - [Authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/): Which login flow to use where, and what each one stores locally. - [API authentication](https://docs.neuralconfig.com/neuralrepo/api/authentication/): Send the same key as X-API-Key from any language. - [CI/CD](https://docs.neuralconfig.com/neuralrepo/guides/ci-cd/): Store a key as a secret and capture ideas from a pipeline. - [Security](https://docs.neuralconfig.com/neuralrepo/architecture/security/): How keys are hashed, scoped, and audited. --- # nrepo link / unlink / links Source: https://docs.neuralconfig.com/neuralrepo/cli/link/ ```bash nrepo link <source-id> <target-id> [options] nrepo link --batch # read a JSON array from stdin nrepo unlink <source-id> <target-id> [options] nrepo links <id> [options] ``` **Caution:** **Creating and deleting relations is a Pro feature.** On the free plan `nrepo link` and `nrepo unlink` fail with `This feature requires a Pro plan. Upgrade at https://neuralrepo.com/settings`. Reading them with `nrepo links` works on any plan, so system-created relations — duplicates, connections, branch parents — are still visible. ## nrepo link | Flag | Type | Default | Description | |---|---|---|---| | `--type <type>` | string | `related` | One of `related`, `blocks`, `inspires`, `supersedes`, `parent`. | | `--note <note>` | string | _(none)_ | A note on the relation. Truncated to 500 characters. | | `--force` | boolean | `false` | Bypass cycle detection — for `supersedes` only, see below. | | `--batch` | boolean | `false` | Bulk mode: read a JSON array of links from stdin. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ```bash # A plain association nrepo link 391 402 # A dependency, with a note nrepo link 391 402 --type blocks --note "Dark mode depends on the theme system" ``` ``` ✓ Linked #391 → #402 (blocks) Note: Dark mode depends on the theme system ``` `duplicate` is not accepted here even though it is a real relation type — only duplicate detection creates those: ``` Invalid type "duplicate". Must be one of: related, parent, blocks, inspires, supersedes ``` ### Relation types | Type | Meaning | Direction | |---|---|---| | `related` | General association | Symmetrical in display | | `blocks` | Source blocks progress on the target | Directed | | `inspires` | Source inspired the target | Directed | | `supersedes` | Source replaces the target | Directed | | `parent` | Source is a child/variant of the target | Directed | | `duplicate` | Two captures of the same idea | Created only by duplicate detection | ### Cycle detection Cycles are checked only for the three types where a loop would be meaningless. The check walks existing relations **of the same type** outward from the target, up to 50 hops. | Type | Checked | `--force` | |---|---|---| | `blocks` | Yes — **hard** | Ignored. The link is refused whatever you pass. | | `parent` | Yes — **hard** | Ignored. The link is refused whatever you pass. | | `supersedes` | Yes — soft | Bypasses the check. | | `related`, `inspires` | No | Not applicable. | ``` This would create a circular blocking chain: #391 → #402 → #391 ``` **Caution:** On a refused `parent` cycle the CLI adds "Use `--force` to bypass this check." Re-running with `--force` fails identically — `parent` cycles are hard-blocked server-side and only `supersedes` can be forced. Restructure the chain instead. An existing relation between the same pair in the same direction also fails, with `Relation already exists`. In `--json` mode that error is reported with `"code": "cycle_detected"` even though no cycle was involved — match on the message, not the code. ### Bulk mode `--batch` reads a JSON array from stdin, so you can create many relations in one request: ```bash echo '[ {"source_idea_id": 391, "target_idea_id": 402, "relation_type": "blocks", "note": "theme first"}, {"source_idea_id": 391, "target_idea_id": 417} ]' | nrepo link --batch ``` ``` ✓ Linked #391 → #402 (blocks) ✗ #391 → #417: Target idea 417 not found 1 created, 1 errors ``` | Detail | Value | |---|---| | Links per call | **50** maximum | | Fields | `source_idea_id` and `target_idea_id` required; `relation_type` defaults to `related`; `note` optional | | Atomicity | None — one result per link, failures do not roll back the rest | | Cycles | Reported as a per-link error, not as a command failure | Empty input, an empty array, or invalid JSON exits 1 with a message before anything is sent. ## nrepo unlink ```bash nrepo unlink 391 402 # ✓ Unlinked #391 ↔ #402 ``` Finds the relation between the two ideas in **either** direction and deletes it. If there are several between the same pair, it removes the first one it finds — outgoing before incoming. With no relation at all: ``` No link found between #391 and #402. Run nrepo links 391 to see existing links. ``` ## nrepo links | Flag | Type | Default | Description | |---|---|---|---| | `--type <type>` | string | _(all)_ | Show only this relation type. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ```bash nrepo links 391 ``` ``` Links for #42 "Rewrite auth flow": Blocks → #43 Add dark mode and a theme picker [exploring] — theme first Related ↔ #18 Session cookie cleanup [captured] Child of ← #7 Auth overhaul epic [shipped] ``` Relations are grouped by type and direction, and each type is labelled from the perspective of the idea you asked about — `Blocks` outgoing versus `Blocked by` incoming, `Parent of` versus `Child of`. `related` and `duplicate` are symmetrical, so they are listed once rather than twice. `--json` returns `{ "outgoing": [...], "incoming": [...] }`, each entry carrying the relation's own `id`, its type, note, score, and the other idea's `idea_id`, `idea_number`, `idea_title`, and `idea_status`. ## Next Steps - [nrepo graph](https://docs.neuralconfig.com/neuralrepo/cli/graph/): Follow these relations outward several hops instead of one. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): What each type means, and which relations the system creates on its own. - [nrepo branch](https://docs.neuralconfig.com/neuralrepo/cli/branch/): Create a parent relation as a side effect of forking an idea. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): See the same graph laid out visually in the web app. --- # nrepo log Source: https://docs.neuralconfig.com/neuralrepo/cli/log/ ```bash nrepo log [options] ``` Lists ideas newest first, by creation date. Archived ideas never appear. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--limit <n>` | number | _(no limit)_ | Stop after `n` ideas. | | `--status <status>` | string | _(all)_ | One of `captured`, `exploring`, `building`, `shipped`, `shelved`. | | `--tag <tag>` | string | _(all)_ | Exact tag name. Combines with `--status`. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | **Caution:** **Without `--limit`, `nrepo log` fetches every idea you have**, paging through the API 100 at a time. On a large repo that is several round trips and a very long scroll. Pass `--limit` unless you actually want the lot — `nrepo log --limit 20` is the usual intent. ## Examples ```bash # The 20 most recent nrepo log --limit 20 # Everything you are currently exploring nrepo log --status exploring # Backend work in progress nrepo log --tag backend --status building # Titles only, for a script nrepo log --limit 50 --json | jq -r '.[].title' ``` ## Output ``` #42 exploring ⬡ Rewrite auth flow [auth, security] #41 building ◎ Refactor user service [backend] #40 shipped ✉ Fix login redirect bug [auth] ``` Each row is `#number`, status padded to ten characters, a glyph for the capture source, the title, and tags in brackets. The source glyphs are `◎` web, `⬡` CLI, `◈` Claude MCP, `◉` Siri, `✉` email, `⚙` API, `⌘` shortcut, `⬢` iOS. With no matches: ``` No ideas found. ``` `--json` prints a flat **array** of idea objects — the pages are already merged, and there is no wrapper object or pagination cursor to handle: ```json [ { "id": 391, "number": 42, "title": "Rewrite auth flow", "status": "exploring", "source": "cli", "tags": ["auth", "security"], "is_archived": 0, "created_at": "2026-07-24 10:00:00", "updated_at": "2026-07-24 11:30:00" } ] ``` **Tip:** `--tag` matches one tag exactly, and there is no OR. For several tags, run the command once per tag and merge, or filter the JSON yourself: `nrepo log --json | jq '[.[] | select(.tags | index("auth") or index("security"))]'`. ## Ordering surprises Ideas sort by `created_at`, not by when you last touched them, so editing an old idea does not move it up the list. [`nrepo merge`](https://docs.neuralconfig.com/neuralrepo/cli/merge/) is the one command that changes the order: the kept idea inherits the **earlier** of the two creation timestamps, so a merge can push it down the list. ## Next Steps - [nrepo search](https://docs.neuralconfig.com/neuralrepo/cli/search/): Find ideas by meaning when you cannot remember the tag or the wording. - [nrepo status](https://docs.neuralconfig.com/neuralrepo/cli/status/): Counts per status, recent captures, and pending duplicates in one screen. - [nrepo move](https://docs.neuralconfig.com/neuralrepo/cli/move/): Change the status of one idea, or of everything a filter returned. - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): What each of the five statuses means and how ideas usually travel between them. --- # nrepo merge Source: https://docs.neuralconfig.com/neuralrepo/cli/merge/ ```bash nrepo merge <keep-id> <absorb-id> [options] ``` Folds the second idea into the first. The kept idea takes both bodies and both tag sets; the absorbed one is shelved and archived. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--force` | boolean | `false` | Suppress the preview block. | | `--json` | boolean | `false` | Output the merged idea as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | **Danger:** **There is no confirmation prompt, and the merge cannot be undone.** `--force` only hides the preview text — with or without it, the merge runs as soon as you press Enter. Run [`nrepo diff <keep-id> <absorb-id>`](https://docs.neuralconfig.com/neuralrepo/cli/diff/) first if you are unsure. ## What a merge does | Field | Result | |---|---| | Body | Kept idea's body, then `---`, then the absorbed idea's body. Empty bodies are skipped. | | Tags | Union of both sets, on the kept idea. | | Relations | Everything pointing at or from the absorbed idea is repointed at the kept idea. Self-references are removed. | | `created_at` | The kept idea takes the **earlier** of the two timestamps. | | Absorbed idea | Status set to `shelved` **and** archived. | | Title | The kept idea's. The absorbed title is discarded. | | Links (URLs) | Stay with the absorbed idea — they are not transferred. | **Caution:** No `supersedes` relation is created between the two, deliberately: the absorbed idea is archived, and archived ideas are filtered out of every relation query, so the relation would be invisible. Do not go looking for one. Because the absorbed idea is archived, it disappears from `nrepo log`, `nrepo search`, and the status counts — this is not the same as manually moving it to `shelved`, which leaves it visible. And because the kept idea inherits the earlier creation date, it may move **down** your `nrepo log`. ## Output ```bash nrepo merge 391 402 ``` ``` Merge preview: Keep: #42 "Rewrite auth flow" [exploring] Absorb: #43 "Auth rewrite — JWT variant" [captured] The absorbed idea will be shelved and archived. Bodies will be concatenated, tags merged. ✓ Merged #43 into #42 Title: "Rewrite auth flow" Tags: auth, security, jwt #43 "Auth rewrite — JWT variant" → shelved (superseded by #42) ``` The preview is printed and the merge proceeds — it is a record of what is about to happen, not a prompt. The closing "superseded by" is wording, not a relation. Two guards do apply before anything is written: identical IDs are rejected with `Cannot merge an idea with itself`, and a missing idea returns `API error (404): One or both ideas not found`. ## Merging a detected duplicate is a different operation [`nrepo duplicate merge`](https://docs.neuralconfig.com/neuralrepo/cli/duplicate/) resolves a detection rather than merging two ideas you chose, and the two behave differently — the duplicate path keeps the **newer** idea and does not transfer relations. Do not treat them as aliases. ## Next Steps - [nrepo diff](https://docs.neuralconfig.com/neuralrepo/cli/diff/): Compare the two ideas before you commit to a merge you cannot undo. - [nrepo duplicate](https://docs.neuralconfig.com/neuralrepo/cli/duplicate/): The other merge path, and how it differs from this one. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): Which idea survives each kind of merge, and what happens to the other. - [nrepo link](https://docs.neuralconfig.com/neuralrepo/cli/link/): Relate two ideas instead of collapsing them into one. --- # nrepo move Source: https://docs.neuralconfig.com/neuralrepo/cli/move/ ```bash nrepo move <id> <status> # one idea nrepo move <status> --ids <ids> # many ideas ``` Which mode you get depends on `--ids`, not on the arguments: with `--ids` present the first positional argument is read as the **status**, without it as the **id**. Valid statuses: `captured`, `exploring`, `building`, `shipped`, `shelved`. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--ids <ids>` | string | _(none)_ | Comma-separated idea IDs. Switches the command into bulk mode. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | Passing neither a second positional argument nor `--ids` prints the usage line and exits 1: ``` Usage: nrepo move <id> <status> or nrepo move <status> --ids 1,2,3 ``` An unknown status is rejected before any request is sent: ``` Invalid status "done". Must be one of: captured, exploring, building, shipped, shelved ``` ## Single mode ```bash nrepo move 391 building ``` ``` ✓ #42 → building ``` `--json` prints the full updated idea. ## Bulk mode ```bash nrepo move shipped --ids 391,402,417 ``` ``` ✓ #391 ✓ #402 ✗ #417 Idea 417 not found 2 moved to shipped, 1 errors ``` **Caution:** Bulk moves are **not atomic**. Each idea is updated independently and you get one result line per ID — a bad ID in the middle does not roll back the ones before it. Read the trailing counts, or the `results` array in `--json`, rather than assuming the whole batch landed. The per-idea lines print the **ID you passed**, not the display number the single-idea form echoes. Do not read `✓ #391` as "idea number 391". `--json` returns the raw bulk response: ```json { "updated": 2, "errors": 1, "results": [ { "id": 391, "status": "updated" }, { "id": 402, "status": "updated" }, { "id": 417, "status": "error", "error": "Idea 417 not found" } ] } ``` ```bash # Fail a CI step if anything in the batch failed nrepo move shipped --ids 391,402 --json | jq -e '.errors == 0' > /dev/null ``` ## Limits | Limit | Value | |---|---| | IDs per bulk request | **50** — more returns `400 Maximum 50 ideas per bulk update` | | ID parsing | Non-numeric and non-positive entries are silently dropped; an empty result exits 1 | For more than 50, batch the list yourself: ```bash nrepo log --status captured --json | jq -r '.[].id' \ | xargs -n 50 | tr ' ' ',' \ | while read -r batch; do nrepo move shelved --ids "$batch"; done ``` ## Next Steps - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): What each status is for and the transitions that matter. - [nrepo tag](https://docs.neuralconfig.com/neuralrepo/cli/tag/): The other bulk editor — same 50-ID cap, same per-idea results. - [nrepo log](https://docs.neuralconfig.com/neuralrepo/cli/log/): Build the ID list a bulk move consumes. - [nrepo rm](https://docs.neuralconfig.com/neuralrepo/cli/rm/): Archive an idea instead of shelving it. --- # nrepo pull Source: https://docs.neuralconfig.com/neuralrepo/cli/pull/ ```bash nrepo pull <id> [options] ``` Writes an idea and its context into a directory as Markdown — the usual way to hand an idea to a coding agent or an editor. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--to <dir>` | string | `.` | Output directory. Created if it does not exist. | | `--json` | boolean | `false` | Print the idea as JSON and write **no** files. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Examples ```bash nrepo pull 391 # ✓ Pulled #42 to /Users/you/work/ # IDEA.md, CONTEXT.md, .neuralrepo nrepo pull 391 --to ./ideas/auth-rewrite # Just the body, no files written nrepo pull 391 --json | jq -r '.body' ``` ## What gets written | File | Written when | Contents | |---|---|---| | `IDEA.md` | Always | Title as an `h1`, then status, source, creation date, tags, and source URL as bold fields, a `---` rule, and the body. An idea with no body gets `_No body_`. | | `CONTEXT.md` | The idea has relations | One bullet per related idea: relation type, title, and similarity score where there is one. | | `RELATED.md` | The idea has attached URLs | One Markdown link per attachment. | | `.neuralrepo` | Always | JSON: `idea_id`, `api_url`, and `pulled_at`. | **Caution:** Existing files are **overwritten without warning**, and stale ones are not cleaned up. Pull an idea whose last relation you removed and the old `CONTEXT.md` stays on disk describing relations that no longer exist. Pull into a fresh directory when that matters. ## There is no push back **Caution:** `.neuralrepo` is written but never read. No command syncs local edits back — editing `IDEA.md` changes nothing on the server, and pulling again silently discards those edits. To send changes back, use [`nrepo edit`](https://docs.neuralconfig.com/neuralrepo/cli/edit/): ```bash nrepo edit 391 --body "$(sed '1,/^---$/d' IDEA.md)" ``` Despite the name, `nrepo pull` and `nrepo push` are not two halves of a sync — `push` creates a new idea, and `pull` exports an existing one. ## Working with an agent ```bash mkdir -p /tmp/idea && nrepo pull 391 --to /tmp/idea claude "Read /tmp/idea/IDEA.md and CONTEXT.md, then draft an implementation plan" ``` The Claude Code skill bundled with the CLI does this for you — see [Claude Code](https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/). ## Next Steps - [nrepo show](https://docs.neuralconfig.com/neuralrepo/cli/show/): The same content in the terminal, without touching the filesystem. - [nrepo edit](https://docs.neuralconfig.com/neuralrepo/cli/edit/): Send an edited body back to the server. - [Idea to Project](https://docs.neuralconfig.com/neuralrepo/guides/idea-to-project/): The full workflow from a captured idea to a working implementation. - [Claude Code](https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/): Let the bundled skill drive pull, search, and push for you. --- # nrepo push Source: https://docs.neuralconfig.com/neuralrepo/cli/push/ ```bash nrepo push <title> [options] ``` Creates an idea with `source: cli`. The title is the only required argument. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--body <text>` | string | _(none)_ | Body text. Markdown is preserved as written. | | `--tag <tag>` | string | _(none)_ | Add a tag. Repeat the flag for multiple tags. | | `--status <status>` | string | `captured` | One of `captured`, `exploring`, `building`, `shipped`, `shelved`. | | `--json` | boolean | `false` | Output the created idea as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Examples ```bash # Title only nrepo push "Add dark mode to dashboard" # With a body, two tags, and a starting status nrepo push "Rewrite auth flow" \ --body "Switch from session cookies to JWT tokens" \ --tag auth --tag security \ --status exploring # A multi-line body, quoted nrepo push "Design notes for v2" --body "$(cat design-notes.md)" ``` **Caution:** There is no stdin mode. `--body` takes a value; `--body -` would create an idea whose body is a single hyphen. Use command substitution — `--body "$(cat file.md)"` — for file contents. ## Output ``` ✓ Idea captured #42 captured ⬡ Rewrite auth flow [auth, security] Processing: embeddings, dedup, and auto-tagging queued ``` The row is the CLI's standard idea line: `#number`, status padded to ten characters, a glyph for the capture source (`⬡` is `cli`), the title, and tags in brackets. The processing line means the idea is already saved — the embedding, duplicate check, and auto-tagging run afterwards on a queue and take a few seconds. ```bash nrepo push "Rewrite auth flow" --tag auth --json ``` ```json { "id": 391, "number": 42, "title": "Rewrite auth flow", "body": null, "status": "captured", "source": "cli", "source_url": null, "vectorize_id": null, "tags": ["auth"], "is_archived": 0, "created_at": "2026-07-24 10:00:00", "updated_at": "2026-07-24 10:00:00", "processing": true } ``` **Caution:** `id` and `number` are different values. `#42` is the display number shown in every listing; `391` is the identifier every other command takes as its `<id>` argument. See [Troubleshooting](https://docs.neuralconfig.com/neuralrepo/cli/troubleshooting/#the-id-argument-is-not-the-number-you-see). Capture the ID for a follow-up command like this: ```bash ID=$(nrepo push "Rewrite auth flow" --json | jq -r '.id') nrepo tag "$ID" auth security ``` ## Limits | Limit | Value | What happens past it | |---|---|---| | Title length | 200 characters | `400` with a field error | | Body length | 50,000 characters | `400` with a field error | | Tags per idea | 20 | `400` with a field error | | Tag name length | 50 characters | `400` with a field error | | Active ideas, free plan | 50 | `403` — "You've captured 50 ideas on the free plan." | Archiving an idea with [`nrepo rm`](https://docs.neuralconfig.com/neuralrepo/cli/rm/) frees a slot against the free-plan cap. ## Next Steps - [nrepo show](https://docs.neuralconfig.com/neuralrepo/cli/show/): Read back the idea you just created, including its links and relations. - [nrepo tag](https://docs.neuralconfig.com/neuralrepo/cli/tag/): Add tags after the fact, to one idea or to fifty. - [nrepo branch](https://docs.neuralconfig.com/neuralrepo/cli/branch/): Fork an idea into a variant instead of pushing a near-duplicate. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): What the queued dedup pass does with the idea seconds after you push it. --- # nrepo rm Source: https://docs.neuralconfig.com/neuralrepo/cli/rm/ ```bash nrepo rm <id> [options] ``` Archives an idea. The row stays in the database, but the idea disappears from every NeuralRepo surface. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--force` | boolean | `false` | Suppress the preview block. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | **Danger:** **There is no confirmation prompt.** `--force` only hides the preview text; the archive happens either way, immediately. The CLI has no restore command: bring an idea back from the **Archive** view in the web app or with [`POST /ideas/:id/restore`](https://docs.neuralconfig.com/neuralrepo/api/ideas/#restore-idea), which also re-indexes it for semantic search. If you only want an idea out of the way, [`nrepo move <id> shelved`](https://docs.neuralconfig.com/neuralrepo/cli/move/) hides it from your working views while leaving it listable, searchable, and reversible. ## Output ```bash nrepo rm 391 ``` ``` Archive preview: #42 "Rewrite auth flow" [exploring] The idea will be archived (soft-deleted). ✓ Archived #42 "Rewrite auth flow" ``` ```bash nrepo rm 391 --force --json ``` ```json { "success": true, "archived": 391 } ``` `archived` echoes the ID you passed, not the display number. ## What archiving changes | Surface | Effect | |---|---| | `nrepo log`, `nrepo status` | Gone from listings and from every status count | | `nrepo search` | Gone — the vector is deleted from the search index on archive | | Relations | Relations to and from it are filtered out of `nrepo links`, `nrepo graph`, and the mind map | | `nrepo show <id>` | Still returns the idea, because it is fetched by ID directly | | Free-plan idea cap | Frees a slot — the 50-idea cap counts unarchived ideas only | ## Next Steps - [nrepo move](https://docs.neuralconfig.com/neuralrepo/cli/move/): Shelve an idea instead — hidden from your working views, and reversible. - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): Where shelved sits among the five statuses, and when to use it. - [nrepo merge](https://docs.neuralconfig.com/neuralrepo/cli/merge/): Fold a redundant idea into another rather than discarding it. - [nrepo pull](https://docs.neuralconfig.com/neuralrepo/cli/pull/): Export an idea to local files before archiving it. --- # nrepo search Source: https://docs.neuralconfig.com/neuralrepo/cli/search/ ```bash nrepo search <query> [options] ``` Runs a **semantic** search: the query is embedded, matched against your idea vectors, filtered by a minimum score, and reranked. Archived ideas are excluded. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--limit <n>` | number | 20 | Maximum results. Values above 100 are clamped to 100. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Output ```bash nrepo search "authentication flow" ``` ``` Search: "authentication flow" (semantic) — 3 results #42 exploring ⬡ Rewrite auth flow [auth, security] (92%) #18 captured ◎ Session cookie cleanup [auth] (78%) #7 shipped ◈ Login redirect bug (65%) ``` The header names the search type that actually ran — `semantic` or `fts`. Each row is the standard idea line: number, status, source glyph, title, tags, and the match score as a percentage. ```bash nrepo search "authentication flow" --json ``` ```json { "query": "authentication flow", "results": [ { "id": 391, "number": 42, "title": "Rewrite auth flow", "status": "exploring", "score": 0.92, "tags": ["auth", "security"] } ], "search_type": "semantic" } ``` **Caution:** `--json` returns an **object**, not an array. Pipe through `.results`: `nrepo search "auth" --json | jq -r '.results[].title'`. ## When it returns nothing **Caution:** A semantic search that finds no match above the score threshold returns an **empty result set**. It does not fall back to keyword search. The agent and the MCP server do fall back; the CLI and the REST API do not, so "no results" from `nrepo search` does not mean the words appear nowhere in your repo. Two common causes: - **The idea is too new.** Embeddings are generated on a queue after capture. An idea pushed seconds ago has no vector yet and cannot be found semantically. `nrepo log --limit 5` will still show it. - **The wording is too distant.** Semantic matching is not substring matching. For a literal term — an error code, a package name — use `nrepo log` and filter, or search from the web app, which exposes a keyword mode the CLI has no flag for. ## Free plan The free plan includes **10 semantic searches per calendar month**. Past that the server runs keyword (FTS) search instead and says so in the response: ``` Search: "authentication flow" (fts) — 2 results ``` In `--json`, the same case adds `semantic_limit_reached: true` and a `semantic_limit_message`. Keyword results carry `"score": null`, so the human rows show no percentage. The counter resets at the start of each month. ## Next Steps - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): How semantic search is built, what the score threshold means, and which surfaces fall back to keyword. - [nrepo log](https://docs.neuralconfig.com/neuralrepo/cli/log/): List ideas by status or tag when you know exactly what you are looking for. - [nrepo show](https://docs.neuralconfig.com/neuralrepo/cli/show/): Open a result in full, with its body, links, and relations. - [nrepo graph](https://docs.neuralconfig.com/neuralrepo/cli/graph/): Walk outward from a result through its connections. --- # nrepo show Source: https://docs.neuralconfig.com/neuralrepo/cli/show/ ```bash nrepo show <id> [options] ``` Prints one idea in full: metadata, body, attached URLs, and related ideas. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | **Caution:** `<id>` is the idea's `id`, not the `#42` shown in listings. They are different numbers on any account that is not the very first in the database. `nrepo log --json | jq '.[] | {id, number, title}'` shows both side by side, and the web app's URL — `neuralrepo.com/ideas/391` — is the `id` too. See [Troubleshooting](https://docs.neuralconfig.com/neuralrepo/cli/troubleshooting/#the-id-argument-is-not-the-number-you-see). ## Output ```bash nrepo show 391 ``` ``` #42 Rewrite auth flow Status: exploring Source: ⬡ cli Created: Jul 24, 2026 Updated: Jul 24, 2026 Tags: auth, security ────────────────────────────── Switch from session cookies to JWT tokens. Short-lived access tokens, refresh in an httpOnly cookie. Links · RFC 8725: https://datatracker.ietf.org/doc/html/rfc8725 Related Ideas · related: Session cookie cleanup (78%) · parent: Auth overhaul epic ``` The two lists are different things: - **Links** are URLs attached to the idea — a repo, a spec, an article. - **Related Ideas** are relations to other ideas. A percentage means the relation was created by duplicate detection or the connection pass with that similarity score; relations you created by hand have none. `URL:` appears above the divider when the idea has a `source_url` — set when it was captured from a web page or the share sheet rather than typed. ## JSON output ```bash nrepo show 391 --json ``` ```json { "id": 391, "number": 42, "title": "Rewrite auth flow", "body": "Switch from session cookies to JWT tokens.", "status": "exploring", "source": "cli", "source_url": null, "tags": ["auth", "security"], "links": [ { "id": 12, "url": "https://datatracker.ietf.org/doc/html/rfc8725", "title": "RFC 8725", "link_type": "url" } ], "relations": [ { "id": 88, "relation_type": "related", "target_idea_id": 402, "related_idea_number": 18, "related_idea_title": "Session cookie cleanup", "score": 0.78 } ], "is_archived": 0, "created_at": "2026-07-24 10:00:00", "updated_at": "2026-07-24 11:30:00" } ``` An idea that does not exist — or belongs to another account — returns the same error either way: ``` API error (404): Idea not found ``` ## Next Steps - [nrepo edit](https://docs.neuralconfig.com/neuralrepo/cli/edit/): Change the title or body of the idea you are looking at. - [nrepo links](https://docs.neuralconfig.com/neuralrepo/cli/link/): The relations list on its own, grouped by direction and type. - [nrepo pull](https://docs.neuralconfig.com/neuralrepo/cli/pull/): Write the same content to local Markdown files for an editor or an agent. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): What each relation type means and which ones the system creates for you. --- # nrepo status Source: https://docs.neuralconfig.com/neuralrepo/cli/status/ ```bash nrepo status [options] ``` A one-screen overview: who you are signed in as, how your ideas split across the five statuses, your five most recent captures, and any pending duplicate detections. ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Output ``` NeuralRepo Dashboard Alice · pro Status breakdown captured ████████████ 12 exploring █████ 5 building ███ 3 shipped ████████ 8 shelved ██ 2 total 30 Recent captures #58 captured ⬡ Async worker queue for tasks #57 captured ◎ Mobile habit tracker redesign #56 captured ◉ API rate limiting improvements Pending duplicates (2) #101 Auth flow redesign ≈ #42 Rewrite auth flow 91% #103 Dark mode support ≈ #40 Add dark mode to dashboard 85% ``` Reading the sections: - **Status breakdown** — one bar character per idea, capped at 40 characters. A repo with 500 `captured` ideas and 3 `shipped` ones shows a 40-character bar next to a 3-character one, so compare the numbers rather than the bars once you are past 40. - **Recent captures** — up to five ideas still in `captured`, in list order. It is *not* a last-7-days window: an idea captured a year ago and never moved still appears here. - **Pending duplicates** — up to five unresolved detections, with the similarity score. The section is omitted entirely when there are none. ## JSON output ```bash nrepo status --json ``` ```json { "user": { "email": "alice@example.com", "plan": "pro" }, "counts": { "captured": 12, "exploring": 5, "building": 3, "shipped": 8, "shelved": 2 }, "total": 30, "pending_duplicates": 2 } ``` The JSON form is a summary only — it carries neither the recent captures nor the duplicate pairs. Use [`nrepo log --limit 5 --json`](https://docs.neuralconfig.com/neuralrepo/cli/log/) and [`nrepo duplicate list --json`](https://docs.neuralconfig.com/neuralrepo/cli/duplicate/) for those. ```bash BUILDING=$(nrepo status --json | jq '.counts.building') echo "You have $BUILDING ideas in progress" ``` **Caution:** `nrepo status` counts by walking your **entire** idea list, 100 at a time, then calls the duplicates and user endpoints. On a large repo it is the slowest command in the CLI — a poor choice for a shell prompt or a tight loop. **Note:** Pending duplicates are Pro-only. On the free plan the duplicates endpoint returns an empty list, so `pending_duplicates` is always `0` and the section never renders — even though detections are still being recorded in the background and appear the moment you upgrade. ## Next Steps - [nrepo log](https://docs.neuralconfig.com/neuralrepo/cli/log/): List the ideas behind the counts, filtered by status or tag. - [nrepo duplicate](https://docs.neuralconfig.com/neuralrepo/cli/duplicate/): Review and resolve the pending detections this dashboard counts. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): The web app's version of this screen, with connections and stale ideas as well. - [nrepo whoami](https://docs.neuralconfig.com/neuralrepo/cli/auth-commands/): Just the account line, including your active idea count against the free cap. --- # nrepo tag Source: https://docs.neuralconfig.com/neuralrepo/cli/tag/ ```bash nrepo tag <id> <tags...> # add tags to one idea nrepo tag add <tag> --ids <ids> # add one tag to many ideas nrepo tag remove <tag> --ids <ids> # remove one tag from many ideas ``` ## Options | Flag | Type | Default | Description | |---|---|---|---| | `--ids <ids>` | string | — | Comma-separated idea IDs. **Required** for `add` and `remove`. | | `--json` | boolean | `false` | Output as JSON. | | `--human` | boolean | — | Force human-readable output. Wins over `--json`. | ## Add tags to one idea ```bash nrepo tag 391 ui frontend ``` ``` ✓ #42 tags: auth, security, ui, frontend ``` The tags are **merged** with what is already there, so this form only ever adds. The echoed list is the full set after the merge. **Caution:** This form reads the idea, unions the tags, and writes the whole list back. If something else tags the same idea in between — the auto-tagging pass on a fresh capture, for instance — that tag is overwritten. On an idea you have just pushed, wait a few seconds or use `tag add` instead, which adds server-side. There is no single-idea removal form. To take one tag off one idea, use the bulk form with a single ID: ```bash nrepo tag remove frontend --ids 391 ``` ## Add or remove across many ideas ```bash nrepo tag add priority-high --ids 391,402,417 ``` ``` ✓ #391 ✓ #402 ✗ #417 Idea 417 not found 2 tagged with "priority-high", 1 errors ``` `remove` prints the same shape, ending `2 untagged "deprecated", 0 errors`. Removing a tag an idea does not have is not an error — it simply does nothing for that idea. **Caution:** Bulk tagging is **not atomic**: one result line per ID, and a failure part-way through leaves the earlier ideas changed. Check the trailing counts or the `results` array. Both bulk forms accept at most **50 IDs** per call; a longer list returns `400 Maximum 50 ideas per bulk update`. The per-idea lines print the ID you passed, not the display number. `--json` returns the bulk response: ```json { "updated": 2, "errors": 1, "results": [ { "id": 391, "status": "updated" }, { "id": 402, "status": "updated" }, { "id": 417, "status": "error", "error": "Idea 417 not found" } ] } ``` ## Limits and cleanup | Limit | Value | |---|---| | Tags per idea | 20 | | Tag name length | 50 characters | | IDs per bulk call | 50 | A tag that ends up on no ideas is deleted automatically after a bulk change, so removing the last use of a tag also removes it from the tag list. **Note:** Running `nrepo tag` with no arguments at all prints nothing and exits 0 — it is not an error and not a help screen. Use `nrepo tag --help` for usage. ## Next Steps - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): How tags are stored and shared, and what auto-tagging adds to an untagged capture. - [nrepo log](https://docs.neuralconfig.com/neuralrepo/cli/log/): Filter by tag, and build the ID list for a bulk tag change. - [nrepo move](https://docs.neuralconfig.com/neuralrepo/cli/move/): The other bulk editor — same 50-ID cap, same per-idea results. - [nrepo push](https://docs.neuralconfig.com/neuralrepo/cli/push/): Tag an idea as you capture it with a repeatable --tag flag. --- # Troubleshooting Source: https://docs.neuralconfig.com/neuralrepo/cli/troubleshooting/ Start here when a command fails, returns the wrong thing, or does nothing at all. **Caution:** Two of these bite hardest. `<id>` is not the `#N` you see in listings, and neither `nrepo rm` nor `nrepo merge` asks for confirmation — both act the moment you press Enter, and neither can be undone. ## The `<id>` argument is not the number you see Every listing prints ideas as `#42`. Every command takes an `<id>`. **They are usually different numbers**, because the display number is a counter per account while the ID is unique across the whole database. Only the first account created ever sees them line up. Passing a display number therefore gives you one of two wrong answers: ```bash nrepo log # #42 exploring ⬡ Rewrite auth flow nrepo show 42 # API error (404): Idea not found ← 42 is someone else's ID, or no one's # ...or it prints a completely different idea of yours ``` Three reliable ways to get a real ID: ```bash # 1. From --json, which carries both nrepo log --limit 10 --json | jq -r '.[] | "\(.id)\t#\(.number)\t\(.title)"' # 2. From the web app's URL — neuralrepo.com/ideas/391 → 391 # 3. From the command that created the idea ID=$(nrepo push "New idea" --json | jq -r '.id') ``` The CLI's own output mixes the two conventions, so read `#N` carefully: | Where | `#N` means | |---|---| | `nrepo log`, `search`, `show`, `status`, `duplicate list` | Display **number** | | `nrepo move --ids`, `nrepo tag add/remove`, `nrepo rm --json` | The **ID** you passed | | `nrepo branch` first line | Source **ID**, then the new idea's **number** | | `nrepo duplicate list` | Both are numbers; the detection ID is not shown at all | ## Error output and exit codes Every failure exits with status **1**; success exits 0. Errors go to stderr, so they never contaminate a `--json` pipeline on stdout. | Situation | Human message | `--json` `code` | |---|---|---| | Not logged in | ``Not logged in. Run `nrepo login` to authenticate.`` | `auth_required` | | Key revoked or invalid | ``Authentication expired. Run `nrepo login` to re-authenticate.`` | `http_401` | | Feature needs Pro | `This feature requires a Pro plan. Upgrade at https://neuralrepo.com/settings` | `http_403` | | Any other API failure | `API error (404): Idea not found` | `http_404` | | No network / DNS / TLS | `Network error: <detail>. Check your internet connection and try again.` | `network_error` | | Anything else | Raw error | `unknown` | With `--json`, the error is one object on stderr: ```json { "error": "Idea not found", "code": "http_404", "status": 404 } ``` ```bash if ! OUT=$(nrepo show 391 --json 2>err.json); then jq -r '.code' err.json fi ``` A few commands validate before calling the API and print a plain message with no `code` — an unparseable ID, an unknown status, an empty `--ids` list, or `nrepo edit` with neither `--title` nor `--body` (that one does carry `"code": "no_input"`). ## Which commands need Pro | Command | Free-plan behaviour | |---|---| | `nrepo link`, `nrepo unlink`, `nrepo link --batch` | `403` — creating and deleting relations is Pro | | `nrepo key create` | `403` — key creation is Pro | | `nrepo duplicate list / dismiss / merge` | Always empty: the duplicates endpoint returns no rows below Pro | | `nrepo search` | Works, but only **10 semantic searches a month**; after that you get keyword results | | `nrepo push` | Works up to **50 active ideas**, then `403 idea_limit_reached` | | Everything else | Unrestricted | `nrepo login` itself has no plan check, so the CLI is fully usable on the free plan — it is the individual features above that are gated. `nrepo links` (reading relations) is not gated either, so system-created connections stay visible. ## Bulk operations are not atomic `nrepo move --ids`, `nrepo tag add`, `nrepo tag remove`, and `nrepo link --batch` all process their input one entry at a time and report per-entry results. A failure in the middle does not roll back what came before. | Operation | Cap per call | On partial failure | |---|---|---| | `move --ids`, `tag add/remove` | 50 IDs | `✓`/`✗` per idea, then `N moved, M errors` | | `link --batch` | 50 links | `✓`/`✗` per link, then `N created, M errors` | Check the counts, or `.errors` in `--json`, rather than the exit status — a batch where every entry failed still exits 0, because the request itself succeeded. ## Commands that look like they did nothing | Symptom | Cause | |---|---| | `nrepo tag` with no arguments prints nothing | The bare form requires both an ID and at least one tag; with neither it exits 0 silently. Use `nrepo tag --help`. | | `nrepo search` returns no results for words you know are there | Semantic search does not fall back to keyword in the CLI, and a just-captured idea has no embedding yet. See [nrepo search](https://docs.neuralconfig.com/neuralrepo/cli/search/). | | `nrepo links` on a fresh branch shows nothing | The `parent` relation is created by the background queue a second or two later. | | `nrepo graph --depth 4` shows only three levels | The text tree stops at the second hop. The deeper nodes are in `--json`. | | `nrepo duplicate list` is always empty | Duplicate detection is Pro-only. | | A merged idea vanished from `nrepo log` | Merging archives the absorbed idea, and archived ideas are excluded from every listing. | ## Login problems | Symptom | What to do | |---|---| | `Login timed out after 120 seconds` | The local callback server closed. Re-run `nrepo login` rather than finishing the browser page. | | Browser never opens | The URL is printed in the terminal — paste it manually. | | No GitHub account | Browser login is GitHub-only. Use `nrepo login --api-key` with a key from the dashboard (Pro). | | Signed in, but every command says "Not logged in" | Check that `~/.config/neuralrepo/config.json` exists and contains an `api_key`; `nrepo logout` replaces the file with `{}`. | | Commands work on your machine, not in CI | CI needs its own key: `echo "$KEY" \| nrepo login --api-key`. | ## Noise in scripted output The weekly version check prints its notice to **stderr**, before the command's own output, so `nrepo log --json | jq .` is unaffected. To silence it entirely — and skip the outbound call to the npm registry — set `NREPO_NO_UPDATE_CHECK=1`. ## Next Steps - [Authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/): Both login flows in full, and what each one writes to disk. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): Every free-plan cap, and what Pro changes on each surface. - [API errors](https://docs.neuralconfig.com/neuralrepo/api/errors/): The status codes and error bodies behind the CLI's messages. - [Installation](https://docs.neuralconfig.com/neuralrepo/cli/installation/): Version checks, the PATH problem, and the bundled Claude Code skill. --- # Core Concepts Source: https://docs.neuralconfig.com/neuralrepo/getting-started/concepts/ NeuralRepo is built around a few core concepts. Understanding them will help you get the most out of the platform. ## Ideas An **idea** is the atomic unit in NeuralRepo. Every idea has: | Field | Description | |-------|-------------| | **Title** | A short, descriptive name (required) | | **Body** | Longer description or notes in Markdown (optional) | | **Status** | Current stage in your workflow | | **Source** | Where the idea was captured from | | **Tags** | User-defined labels for categorization | | **Relations** | Connections to other ideas | | **Links** | External URLs and references | Ideas are designed to be lightweight. Capture first, organize later. ## Status Workflow Every idea is in exactly one of five statuses. The usual path runs left to right, but **nothing enforces the order** — any status can move to any other status. ``` captured → exploring → building → shipped ↺ (any status) ⇄ shelved ``` | Status | Meaning | |--------|---------| | `captured` | Just recorded — hasn't been evaluated yet | | `exploring` | Actively thinking about or researching this idea | | `building` | Implementation is underway | | `shipped` | The idea has been realized and delivered | | `shelved` | Parked for later — not abandoned, just not active | **Tip:** You can move an idea to `shelved` from any status — and back out again. Shelved ideas are not deleted: they stay searchable, though duplicate detection skips them when comparing a new idea against your existing ones. The web dashboard provides a **kanban board** view where you can drag and drop ideas between status columns. ## Sources The **source** field records where an idea was originally captured. It is set automatically and cannot be changed after creation. | Source | Origin | |--------|--------| | `web` | Created in the web dashboard | | `cli` | Created via the `nrepo` CLI | | `claude-mcp` | Created by Claude AI through MCP | | `siri` | Created via Siri Shortcuts | | `email` | Created by sending an email to your personal capture address (`idea+…@neuralrepo.com`) | | `api` | Created via the REST API directly, and by the AI agent's own tools | | `shortcut` | Created via an Apple Shortcut (non-Siri) | | `ios` | Reserved for the native NeuralRepo iOS app, which is not yet released | Sources are useful for filtering and understanding your capture habits. You can filter by source in the web dashboard and in the API. A source is chosen by the client that creates the idea, so a REST API caller may set any of these values on itself. ## Tags **Tags** are user-defined labels that help you categorize and filter ideas. They are flexible and freeform — use whatever taxonomy makes sense for you. - Lowercase, hyphenated names are the convention (`side-project`, not `Side Project`) - Each idea can have up to **20 tags**, each up to 50 characters - An idea saved with **no** tags is auto-tagged in the background — with AI on Pro, by borrowing tags from its nearest neighbours on the free plan - Filter by tags in the web dashboard, CLI, and API Example tags: `side-project`, `ai`, `mobile`, `revenue`, `devtools`, `ux-improvement` ## Relations **Relations** create typed, directional connections between ideas. Every relation has a **source idea**, a **target idea**, and a **type**. | Type | Meaning | Example | |------|---------|---------| | `related` | General association | "Auth service" ↔ "OAuth provider research" | | `parent` | Hierarchical grouping | "Mobile app" → "Push notifications feature" | | `blocks` | Dependency | "Design system" blocks "Dashboard redesign" | | `inspires` | One idea led to another | "Competitor analysis" inspires "Freemium pricing model" | | `duplicate` | Same idea captured twice | Auto-detected or manually set | | `supersedes` | Replaces an older idea | "Auth v2" supersedes "Auth v1" | Relations are **directional** — the source and target matter. For example, "A blocks B" means A must be completed before B can proceed. The `related` type is the exception; it is treated as bidirectional. Some relations are created for you: `related` links come from similarity scoring, and giving an idea a `parent_id` creates a `parent` relation. Creating relations **by hand** — in the web app, the CLI, the API, or MCP — requires **Pro**; free accounts see the graph read-only. **Note:** A detected duplicate does **not** produce a `duplicate` relation. NeuralRepo records a pending *duplicate detection* you resolve by merging or dismissing, and (because the pair also clears the related threshold) links the two ideas with a `related` relation. The `duplicate` relation type exists for links you create yourself. ## Links **Links** attach external resources to an idea. Each link has a **type** and a **URL**. | Type | Use Case | |------|----------| | `url` | Any web URL — articles, docs, references | | `claude-chat` | Link to a Claude conversation that relates to the idea | | `github-repo` | Link to a GitHub repository | | `github-issue` | Link to a specific GitHub issue or PR | An idea can have multiple links. Links help bridge the gap between ideation and execution — connect your idea to the code, conversation, or research that supports it. Links are **URLs only** — every link type stores an address, not a file. (The API also accepts a fifth type, `attachment`, but nothing in the product uploads files today.) ## Duplicate Detection NeuralRepo uses **vector similarity** to detect potential duplicates. Detection is asynchronous — it runs after the idea is saved, never before: 1. Saving an idea queues it; the queue worker generates a vector embedding of title and body 2. The embedding is compared against your **five nearest** ideas, skipping shelved ones 3. A match above the dedup threshold (default 0.75) records a **pending duplicate detection** 4. You resolve it by merging or dismissing, from the **Duplicates** or **Review** view, the CLI, or the API Duplicate detection is a **Pro feature**, but it runs for everyone: detections are recorded in the background on the free plan and simply not shown, so they are all waiting for you the moment you upgrade. Marking two ideas as duplicates by hand needs Pro too, because it means creating a relation. ## Idea Graph The **idea graph** is the network formed by all your ideas and their relations. NeuralRepo visualizes this as an interactive **mind map** in the web dashboard. The mind map lets you: - See clusters of related ideas at a glance - Discover hidden connections between projects - Identify blocking dependencies - Navigate between ideas by clicking nodes The mind map lives in the web dashboard under the **Mind Map** tab. Free-tier users get **view-only** access; editing the graph (dragging, linking, status changes) and the AI agent require **Pro**. ## AI Agent The **AI agent** is a conversational assistant built into the web app. You can ask it to search your ideas, create new ones, discover connections, brainstorm variations, summarize clusters, score ideas, and draft documents — all in natural language. The agent is available in two places: - **Agent** — a full split-pane view with a chat panel and an artifact canvas - **Mind Map chat panel** — a floating overlay on the mind map where tool results animate directly onto the graph The agent supports five models: Anthropic's Sonnet, Haiku, and Opus via [BYOK](https://docs.neuralconfig.com/neuralrepo/integrations/byok/), plus two Workers AI models — **GPT-OSS 120B** (the default) and **Qwen 30B** — that need no API key. See the [AI Agent](https://docs.neuralconfig.com/neuralrepo/agent/overview/) section for complete documentation. **Start Capturing** Now that you understand the concepts, head to the [Quick Start](https://docs.neuralconfig.com/neuralrepo/getting-started/quickstart/) to begin capturing ideas. **Explore the API** Build custom integrations using the [REST API](https://docs.neuralconfig.com/neuralrepo/api/overview/) with full access to ideas, relations, and links. --- # What is NeuralRepo? Source: https://docs.neuralconfig.com/neuralrepo/getting-started/introduction/ NeuralRepo is a **repo for your ideas**. It captures ideas from wherever inspiration strikes — your terminal, a conversation with Claude AI, a Siri command on your watch, or a quick email — and organizes them with semantic intelligence so nothing gets lost. Think of it as version control for your brain. ## Key Features **Multi-Source Capture** Capture ideas from the web dashboard, CLI, Claude AI via MCP, Siri Shortcuts, email, or the REST API. Every source funnels into one unified repository. **Semantic Search** Go beyond keyword matching. NeuralRepo uses vector embeddings to find ideas by meaning, so searching "mobile app for tracking habits" finds your idea titled "daily routine tracker." **Duplicate Detection** When you save a new idea, NeuralRepo automatically checks for similar existing ideas using vector similarity. No more scattered duplicates across notebooks and apps. **Idea Relations & Graph** Connect ideas with typed relations — parent, blocks, inspires, supersedes, and more. Visualize your idea network as an interactive mind map. **Kanban Workflow** Move ideas through a clear lifecycle: **captured**, **exploring**, **building**, **shipped**, or **shelved**. Drag and drop on the kanban board to update status. **BYOK AI** Bring Your Own Key. Add an Anthropic, OpenAI, or OpenRouter key to unlock the Anthropic models in the AI agent and the "develop this idea" spec generator. Search, duplicate detection, and auto-tagging never need a key — they run on Workers AI. ## Capture From Anywhere NeuralRepo meets you where ideas happen: | Source | How It Works | |--------|-------------| | **Web App** | Full dashboard at [neuralrepo.com](https://neuralrepo.com) with kanban board, search, and mind map | | **CLI** | `nrepo push "My idea"` from any terminal — perfect for developers | | **Claude AI (MCP)** | Tell Claude "save this as an idea" and it writes directly to NeuralRepo | | **Siri Shortcuts** | "Hey Siri, NeuralRepo" for hands-free capture on iPhone, iPad, Mac, or Apple Watch | | **Email** | Send to your personal `idea+…@neuralrepo.com` address from Settings — subject becomes the title, body becomes the content | | **REST API** | Full programmatic access for custom integrations and automations | Every capture channel writes into the same repository, and each idea records the channel it came from in its [`source` field](https://docs.neuralconfig.com/neuralrepo/getting-started/concepts/#sources). ## Architecture NeuralRepo is built on **Cloudflare Workers**, delivering an edge-first architecture that keeps latency low regardless of where you are in the world. Ideas are processed at the edge, embeddings are generated asynchronously, and the entire platform scales effortlessly. **Tip:** NeuralRepo processes ideas asynchronously after capture. When you create an idea via CLI or API, it returns immediately with `processing: true` while embeddings and duplicate detection run in the background. ## Next Steps - [Quick Start](https://docs.neuralconfig.com/neuralrepo/getting-started/quickstart/): Sign up, install the CLI, and capture your first idea in under 5 minutes. - [CLI Reference](https://docs.neuralconfig.com/neuralrepo/cli/installation/): Complete reference for all nrepo commands and options. - [API Reference](https://docs.neuralconfig.com/neuralrepo/api/overview/): Full REST API documentation with examples. - [MCP Integration](https://docs.neuralconfig.com/neuralrepo/capture/mcp/): Connect Claude AI to NeuralRepo via the Model Context Protocol. --- # Plans & Pricing Source: https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/ NeuralRepo offers a generous free tier with access to all core capture channels and a Pro plan for power users who want unlimited ideas and advanced AI features. **Tip:** All new accounts include a **30-day Pro trial** — experience every feature before deciding. ## Plans Overview **Free** **$0 / forever** Capture ideas from anywhere: - Up to **50 ideas** - Web app, CLI, MCP, Claude Code skill - Keyword search (unlimited) - Semantic search (10/month) - Kanban board - Idea relations & mind map (view-only) - Auto-tagging from your nearest ideas - Review queue (stale ideas + new connections) - Data export (JSON, CSV, Markdown) **Pro** **$7/month** or **$70/year** (save $14) Everything in Free, plus: - **Unlimited ideas** - **Unlimited semantic search** - Automatic duplicate detection - AI auto-tagging (context-aware) - Siri Shortcuts & email capture - Full interactive mind map & idea relations - **AI agent** (Agent view + mind map chat) - **Intelligence tools** (semantic layout, synthesis, scoring, contradictions, document drafting) - BYOK AI (bring your own Anthropic/OpenAI/OpenRouter key) - API access & key provisioning ## Feature Comparison | Feature | Free | Pro | |---------|:----:|:---:| | Ideas | 50 | Unlimited | | Web app | Yes | Yes | | Claude MCP connector | Yes | Yes | | Claude Code skill | Yes | Yes | | nrepo CLI | Yes | Yes | | Siri Shortcuts | -- | Yes | | Email capture | -- | Yes | | Keyword search | Yes | Yes | | Semantic search | 10/month | Unlimited | | Duplicate detection | Recorded, hidden | Yes | | Auto-tagging | Borrowed from neighbours | AI, context-aware | | Review queue | Stale + connections | Adds duplicates | | AI agent chat | -- | Yes | | Kanban board | Yes | Yes | | Idea relations | View | Full | | Mind map | View | Full | | JSON / CSV / Markdown export | Yes | Yes | | BYOK (own API keys) | -- | Yes | | API keys | The one `nrepo login` mints | Generate your own in Settings | ## Billing - **Monthly plan**: $7/month, billed monthly. Cancel anytime. - **Annual plan**: $70/year, billed annually. That is $5.83/month — a savings of $14 per year. - Payments are processed securely via Stripe. - You can upgrade, downgrade, or cancel from **Settings > Billing** in the web dashboard. **Tip:** Annual billing saves you two months of cost compared to the monthly plan. ## Frequently Asked Questions ### Can I export my data on the free plan? Yes. Export as JSON, CSV, or a Markdown archive (one Obsidian-ready note per idea, with frontmatter and `[[wiki-links]]` for relations) on every plan. You always own your data. ### What happens if I downgrade from Pro to Free? Your ideas are **preserved and never deleted**. What changes: - **Creating** new ideas is blocked while you hold more than 50. Everything else about your existing ideas still works — edit them, retag them, move their status, archive them, search them, export them. - Relations and the mind map become **view-only**. - Duplicate detections stop being shown (they keep being recorded). - Auto-tagging switches from AI to neighbour-borrowing. - The AI agent, BYOK keys, and email capture become unavailable. Stored BYOK keys are kept, just hidden. - API keys you already have keep working, at the free rate limit of 100 requests a day. Get back under 50 active ideas — archive a few — and capture resumes without upgrading. ### What happens when my Pro trial ends? Your account switches to the free plan. All your data is preserved. You keep access to all core capture channels (web, CLI, MCP, Claude Code). Features like unlimited semantic search, duplicate detection, auto-tagging, and AI agent switch to free-tier limits or become unavailable. You'll see warnings at 5 days and 1 day before expiration. ### Can I use the CLI and MCP on the free plan? Yes. All core capture channels — web app, nrepo CLI, Claude MCP connector, and Claude Code skill — are available on the free plan. The 50-idea limit is the primary gating mechanism. `nrepo login` mints its own API key for you, so the CLI works without a Pro plan even though generating keys by hand in **Settings ▸ API Keys** is a Pro feature. Two MCP tools, `link_ideas` and `unlink_ideas`, are Pro-only because they create relations. ### How does the semantic search limit work on the free plan? You get **10 semantic searches per calendar month**, counted in UTC and reset on the 1st. The eleventh search returns keyword results instead, with a notice explaining why. The counter is incremented when the search starts, so a semantic search that matches nothing still uses one. ### Do you offer team or enterprise plans? Not yet. NeuralRepo is currently designed for individual use. Team features are on the roadmap. ### How do I cancel? Go to **Settings > Billing** and click **Cancel Subscription**. Your Pro features remain active until the end of your current billing period. After that, your account reverts to the free tier. ## Next Steps - [Quick Start](https://docs.neuralconfig.com/neuralrepo/getting-started/quickstart/): Sign up, install the CLI, and capture your first idea in under 5 minutes. - [Core Concepts](https://docs.neuralconfig.com/neuralrepo/getting-started/concepts/): Ideas, statuses, tags, relations, and how they fit together. - [AI Agent Overview](https://docs.neuralconfig.com/neuralrepo/agent/overview/): The largest Pro feature — what it can do and which models it runs on. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): A Pro-and-free feature worth knowing about before you decide. --- # Quick Start Source: https://docs.neuralconfig.com/neuralrepo/getting-started/quickstart/ Get from zero to capturing ideas in minutes. This guide walks you through signing up, creating your first idea from the web and CLI, and optionally connecting Claude AI. 1. **Sign up at neuralrepo.com** Head to [neuralrepo.com](https://neuralrepo.com) and create your account. You can sign up with: - **GitHub** — recommended for developers - **Google** - **Apple** - **Magic link** — passwordless email login No credit card required. Every new account starts with a **30-day Pro trial**. The free tier includes 50 ideas and full access to all capture channels. 2. **Create your first idea from the web dashboard** Once signed in, click **+ Capture** at the top of the left sidebar. The capture form asks for: - **Title** — a short, descriptive name for your idea (required) - **Notes** — optional details in Markdown - **Status** — `captured` (the default), `exploring`, or `building` - **Tags** — type a name and press Enter, or click one of your existing tags Hit **Capture** and your first idea is live. Duplicate detection runs a few seconds later in the background, so a possible-duplicate pair shows up in **Duplicates** and **Review** rather than blocking the save. 3. **Install the CLI** Open your terminal and install the NeuralRepo CLI globally: ```bash npm install -g @neuralconfig/nrepo ``` Verify the installation: ```bash nrepo --version ``` 4. **Log in from the CLI** Authenticate the CLI with your NeuralRepo account: ```bash nrepo login ``` This opens your browser for OAuth authentication. Once approved, the CLI stores your session locally in `~/.config/neuralrepo/config.json`. **Tip:** `nrepo login` works on every plan — the browser flow mints a CLI key for you. It signs in through **GitHub** whichever way you signed up, and finds your account by GitHub's verified primary email, so that email must be the one you signed up with (otherwise it creates a separate account). To paste a key you generated yourself instead, run `nrepo login --api-key` (a bare flag, no value) and paste it at the prompt. Generating keys by hand in **Settings ▸ API Keys** is a Pro feature. 5. **Capture an idea from the CLI** Push your first idea directly from the terminal: ```bash nrepo push "Build a habit tracker app" --tag side-project --status exploring ``` You should see output confirming the idea was created: ``` ✓ Idea captured #42 exploring ⬡ Build a habit tracker app [side-project] Processing: embeddings, dedup, and auto-tagging queued ``` The number after `#` is the idea number you use in every other command. 6. **Search your ideas** Find ideas by meaning: ```bash nrepo search "habit tracker" ``` `nrepo search` runs **semantic** search — vector similarity, so it matches meaning rather than words. Every plan gets it; the free plan gets 10 semantic searches a month and then falls back to keyword search automatically. **Caution:** If semantic search finds nothing above the score threshold, `nrepo search` returns **no results** — it does not silently retry as a keyword search. Search the web app in keyword mode (or call the API with `mode=keyword`) when you know the exact wording. 7. **Connect Claude AI via MCP (optional)** If you use Claude, you can let it read and write ideas directly through the Model Context Protocol. **Claude.ai** In Claude.ai settings, add a new **remote MCP server** with this URL: ``` https://neuralrepo.com/mcp/v2 ``` On first use, Claude will prompt you to authorize NeuralRepo via OAuth. **Claude Code** Add the MCP server from your terminal: ```bash claude mcp add --transport http neuralrepo https://neuralrepo.com/mcp/v2 ``` The next time you start a Claude Code session, it will have access to your NeuralRepo ideas. Once connected, you can say things like *"Save this as an idea in NeuralRepo"* or *"Search my ideas for authentication patterns"* and Claude handles the rest. 8. **Try the AI agent (Pro)** On Pro, the NeuralRepo web app includes a built-in AI agent. Open the **Agent** tab in the sidebar to start a conversation, or open the **Mind Map** and click the agent icon in the bottom-right corner. Select a model — the Workers AI models (**GPT-OSS 120B**, the default, and **Qwen 30B**) work without any API key. For the Anthropic models (Sonnet, Haiku, Opus), add your own key first in **Settings ▸ AI Providers**. Try asking: - *"What am I working on?"* - *"Find connections between my recent ideas"* - *"Brainstorm 3 variations of my top idea"* See the [AI Agent](https://docs.neuralconfig.com/neuralrepo/agent/overview/) docs for the full list of capabilities. ## Next Steps - [Core Concepts](https://docs.neuralconfig.com/neuralrepo/getting-started/concepts/): Understand ideas, statuses, tags, relations, and the idea graph. - [CLI Reference](https://docs.neuralconfig.com/neuralrepo/cli/installation/): Explore all available nrepo commands. - [MCP Integration](https://docs.neuralconfig.com/neuralrepo/capture/mcp/): Deep dive into the Claude AI integration. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): Compare free and Pro features. --- # CI/CD Integration Source: https://docs.neuralconfig.com/neuralrepo/guides/ci-cd/ NeuralRepo's REST API integrates cleanly into CI/CD pipelines. You can automatically create ideas, update statuses, and track project lifecycle events from GitHub Actions, GitLab CI, or any automation platform. **Note:** **CI integration requires Pro.** Creating an API key is a Pro-gated operation (`POST /user/api-keys`), and the **Settings ▸ Connections** tab that manages keys is Pro-only. There is no way to mint a key for CI on the Free plan. ## Setting Up API Key Auth for CI 1. **Create a dedicated API key.** Go to **Settings ▸ Connections ▸ API Keys** in the NeuralRepo web dashboard, enter a descriptive label like `github-actions` or `ci-pipeline`, and click **Generate new API key**. The key is shown once. It starts with `nrp_` and is 68 characters long — copy it before closing the dialog. `nrepo key create <label>` does the same thing from a terminal. 2. **Store as a secret.** Add the key as a repository secret in your CI provider: - **GitHub Actions:** Repository Settings > Secrets > New repository secret. Name it `NEURALREPO_API_KEY`. - **GitLab CI:** Settings > CI/CD > Variables. Add `NEURALREPO_API_KEY` as a masked variable. 3. **Use in your pipeline.** Reference the secret in your workflow file. **Caution:** Never commit API keys to your repository. Always use your CI provider's secret management. ## GitHub Actions Examples ### Create an Idea on Release Automatically capture a new idea when a GitHub Release is published: ```yaml name: NeuralRepo — Track Release on: release: types: [published] jobs: track-release: runs-on: ubuntu-latest steps: - name: Create NeuralRepo idea run: | curl -s -X POST "https://neuralrepo.com/api/v1/ideas" \ -H "X-API-Key: ${{ secrets.NEURALREPO_API_KEY }}" \ -H "Content-Type: application/json" \ -d '{ "title": "Release: ${{ github.event.release.tag_name }}", "body": "${{ github.event.release.body }}", "tags": ["release", "${{ github.event.repository.name }}"], "status": "shipped", "source_url": "${{ github.event.release.html_url }}" }' ``` ### Update Status on Deploy Mark an idea as `shipped` when a deployment succeeds: ```yaml name: Deploy & Update NeuralRepo on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy application run: | # Your deployment steps here echo "Deploying..." - name: Update NeuralRepo idea status if: success() run: | curl -s -X PATCH "https://neuralrepo.com/api/v1/ideas/${{ vars.NEURALREPO_IDEA_ID }}" \ -H "X-API-Key: ${{ secrets.NEURALREPO_API_KEY }}" \ -H "Content-Type: application/json" \ -d '{ "status": "shipped" }' ``` **Tip:** Store the NeuralRepo idea ID as a GitHub Actions variable (`vars.NEURALREPO_IDEA_ID`) so it is easy to update across workflows. ### Search and Annotate on PR Search for related ideas when a PR is opened and post them as a comment: ```yaml name: NeuralRepo Context on: pull_request: types: [opened] jobs: find-context: runs-on: ubuntu-latest steps: - name: Search NeuralRepo for related ideas id: search run: | RESULT=$(curl -s "https://neuralrepo.com/api/v1/ideas/search?q=${{ github.event.pull_request.title }}&limit=3" \ -H "X-API-Key: ${{ secrets.NEURALREPO_API_KEY }}") echo "result=$RESULT" >> "$GITHUB_OUTPUT" - name: Post comment with related ideas uses: actions/github-script@v7 with: script: | const result = JSON.parse('${{ steps.search.outputs.result }}'); if (result.results && result.results.length > 0) { const ideas = result.results.map(i => `- **#${i.id}**: ${i.title} (${i.status})` ).join('\n'); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: `## Related NeuralRepo Ideas\n${ideas}` }); } ``` ## JSON Output Mode When parsing API responses in CI scripts, use `jq` for reliable JSON extraction: ```bash # Create an idea and capture the returned ID IDEA_ID=$(curl -s -X POST "https://neuralrepo.com/api/v1/ideas" \ -H "X-API-Key: $NEURALREPO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Automated release tracking", "status": "captured"}' \ | jq -r '.id') echo "Created idea: $IDEA_ID" # Use the ID in subsequent steps curl -s -X PATCH "https://neuralrepo.com/api/v1/ideas/$IDEA_ID" \ -H "X-API-Key: $NEURALREPO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"status": "shipped"}' ``` ## Best Practices - **One key per pipeline.** Create a separate API key for each CI environment so you can revoke individually. - **Use descriptive tags.** Tag CI-created ideas with `ci`, the repository name, and the event type (e.g., `release`, `deploy`). - **Handle failures gracefully.** Wrap NeuralRepo API calls in `if: success()` conditions so they only run after your primary workflow succeeds. - **Rate limits.** The API allows 10,000 requests per day on Pro, counted per user across every key. Only `X-API-Key` and `Bearer` requests count — web sessions are exempt — so a CI pipeline shares its budget with the CLI and any MCP client on the same account. Exceeding it returns `429` with no `Retry-After` header; the counter resets at UTC midnight. - **Send `X-API-Key`, not `Bearer`, for `nrp_` keys.** The `X-API-Key` header is only inspected when the value starts with `nrp_`; MCP OAuth tokens go in `Authorization: Bearer` instead. **Caution:** **Check the response body, not just the status.** Several NeuralRepo operations report failure inside a `2xx` — bulk updates return a per-idea `results` array, and a key test returns `{"ok": false}` with a `200`. A CI step that only checks the HTTP status will pass over a half-applied batch. See [Errors](https://docs.neuralconfig.com/neuralrepo/api/errors/). ## Next Steps - [API Overview](https://docs.neuralconfig.com/neuralrepo/api/overview/): Full API reference with authentication, pagination, and response formats. - [Rate Limits](https://docs.neuralconfig.com/neuralrepo/api/rate-limits/): How the daily counter works and which requests count against it. - [CLI Installation](https://docs.neuralconfig.com/neuralrepo/cli/installation/): Install the nrepo CLI for local and CI usage. - [Migration](https://docs.neuralconfig.com/neuralrepo/guides/migration/): Bulk-import existing ideas from Notion, Obsidian, or JSON. --- # Claude Code Workflow Source: https://docs.neuralconfig.com/neuralrepo/guides/claude-code-workflow/ The most productive NeuralRepo workflow combines **Claude for brainstorming** with **Claude Code for building**. This guide walks through the full pattern. ## The Pattern The workflow has four phases: 1. **Brainstorm in Claude** — explore ideas conversationally. 2. **Save to NeuralRepo** — persist the best ideas via MCP. 3. **Switch to Claude Code** — pull ideas into a project directory. 4. **Build with context** — Claude Code uses the idea as a spec. This creates a clean separation: Claude is your thinking partner, NeuralRepo is your memory, and Claude Code is your builder. ## Prerequisites - NeuralRepo MCP server connected to both Claude.ai and Claude Code. See the [MCP setup guide](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/). - The nrepo CLI installed. See [CLI installation](https://docs.neuralconfig.com/neuralrepo/cli/installation/). - **Pro**, if you want the linking step — `link_ideas` is the one tool in this workflow that Free cannot call. Everything else works on any plan. **Caution:** Throughout this guide, let Claude hold the identifier rather than retyping the number you see. Claude's summaries usually quote the display `#number`, while every tool and CLI command keys off the database `id`, and the two differ on any account but the first. "That idea" is safer than "idea 63" — see [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). ## Phase 1: Brainstorm in Claude Start a conversation in Claude.ai and think through your idea: > **You:** I want to build a CLI tool that generates changelogs from git commits. It should group commits by type (feat, fix, chore) and support conventional commits. What do you think? > > **Claude:** That is a solid idea. Here are some considerations... > - Parse conventional commit prefixes (feat:, fix:, chore:, etc.) > - Group by type with headings > - Support a date range filter > - Output as markdown > - Optional: detect breaking changes from BREAKING CHANGE footers Iterate until the idea is well-formed. ## Phase 2: Save to NeuralRepo Once you have something worth keeping, ask Claude to save it: > **You:** Save this as an idea in NeuralRepo. Tag it with `cli`, `devtools`, and `mvp`. > > **Claude:** *(calls `save_idea`)* Saved as idea #63 (id 163): "CLI changelog generator from git commits" with tags `cli`, `devtools`, `mvp`. **Tip:** Ask Claude to search before saving: "Do I already have an idea about changelog generation?" This prevents duplicates and may surface related ideas you forgot about. If related ideas exist, link them (Pro): > **You:** Link this to the git workflow tools idea. > > **Claude:** *(calls `link_ideas`)* Linked them with relation type "related." The duplicate detector will often have made that connection for you already: any pair scoring above your related threshold gets a system-created `related` relation within seconds of the save. ## Phase 3: Switch to Claude Code Open your terminal, navigate to your project directory, and start Claude Code: ```bash mkdir changelog-cli && cd changelog-cli claude ``` Now pull the idea into context: ``` You: Search my NeuralRepo for the changelog generator idea Claude Code: [calls search_ideas with query "changelog generator"] Found idea #63 (id 163): "CLI changelog generator from git commits" (captured) Tags: cli, devtools, mvp Body: Parse conventional commit prefixes, group by type... You: Pull that idea into this project Claude Code: [runs: nrepo pull 163 --to ./] Created ./idea-163-cli-changelog-generator.md You: Read that file and set up the project — initialize a Node.js package, create the CLI entry point, and implement the core logic. ``` ## Phase 4: Build with Context Claude Code now has the full idea context and can build accordingly. As you work, keep the idea status updated: ``` You: Move that idea to building Claude Code: [calls update_idea with id=163, status="building"] Updated. ``` When you ship: ``` You: Mark it shipped and add the npm package URL to the body Claude Code: [calls update_idea with id=163, status="shipped", body="... Published as changelog-gen on npm."] Done. ``` **Caution:** `update_idea` replaces the whole `body`. Ask Claude to fetch the current body and append to it, rather than passing a one-line update that silently discards everything you wrote before. ## Searching Before New Work Before starting any new project, make it a habit to search NeuralRepo: ``` You: Before we start, search my NeuralRepo for anything related to "API monitoring" or "endpoint health checks" Claude Code: [calls search_ideas] Found 3 related ideas: - #14: "API uptime dashboard" (exploring) - #31: "Health check endpoint standard" (captured) - #45: "Alerting system for API failures" (shelved) ``` This surfaces prior thinking and prevents you from rebuilding what you have already explored. ## Full Conversation Example Here is a complete session across both tools: **In Claude.ai:** ``` You: I need a GitHub Action that posts PR size labels (S, M, L, XL) based on lines changed. Let's think through the design. Claude: [discusses design, thresholds, label colors, edge cases] You: Great, save this to NeuralRepo tagged with github, actions, devtools Claude: [saves idea #71] ``` **In Claude Code:** ``` You: Search NeuralRepo for the PR size labels idea Claude Code: [finds idea #71, displays details] You: Pull it and build a GitHub Action based on the spec Claude Code: [pulls idea, scaffolds the action, implements logic] You: Looks good. Move that idea to building. Claude Code: [updates status] ... after testing and publishing ... You: Mark it shipped Claude Code: [updates to shipped] ``` ## Tips - **Use tags consistently.** Tags like `mvp`, `v2`, and `blocked` help you filter ideas in Claude Code searches. - **Link related ideas.** When Claude Code finds multiple related ideas, link them so the graph builds naturally. - **Keep the body updated.** As you learn things during building, update the idea body. It becomes a project journal. - **Review weekly.** Use the [weekly digest](https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/) to stay on top of stale ideas. ## Next Steps - [Claude Code Integration](https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/): Detailed setup guide for connecting NeuralRepo to Claude Code. - [Idea to Project](https://docs.neuralconfig.com/neuralrepo/guides/idea-to-project/): The full lifecycle workflow from captured idea to shipped project. - [MCP Server Reference](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): Every tool this workflow calls, with parameters and response shapes. - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): How the search-before-you-save habit actually resolves matches. --- # Building an Idea Graph Source: https://docs.neuralconfig.com/neuralrepo/guides/idea-graph/ NeuralRepo is not just a list of ideas — it is a **graph**. By connecting ideas with typed relations, you build a navigable network that reveals how your thinking fits together. **Note:** **Creating and removing relations requires Pro.** Every write on `/map/relations`, the MCP `link_ideas` and `unlink_ideas` tools, and `nrepo link`/`nrepo unlink` are all Pro-gated. Free accounts can read and explore the graph, including the system-created relations described below. ## Relation Types You can create **five** relation types. Every one is directional in storage; only `related` is symmetric in meaning. | Relation | Direction | When to Use | |----------|-----------|-------------| | `related` | Symmetric | Two ideas share a theme or domain but neither depends on the other. | | `parent` | **Source is a child of target** | Break a large idea into sub-ideas. Link each child *to* the umbrella idea. | | `blocks` | Source blocks target | Target cannot proceed until source is resolved. Use for dependencies. | | `inspires` | Source inspired the target | One idea led to another. Use for variants, evolutions, or spin-offs. | | `supersedes` | Source replaces target | The source idea is a newer, better version. The target can be shelved. | **Caution:** **`parent` runs child → parent, which is the opposite of how it reads aloud.** `nrepo link 55 42 --type parent` makes **55 a child of 42**, not the other way round. Get it backwards and your breakdown inverts silently — the link is valid either way. A sixth value, `duplicate`, exists in the database's type constraint and is rejected everywhere you could type it (`Invalid type "duplicate"`). Nothing creates it either: when duplicate detection fires it writes a row to `duplicate_detections` and, separately, a system-created **`related`** relation. Treat `duplicate` as a dead value rather than a relation you might encounter. ## Creating Relations ### From the CLI ```bash # Link two ideas as related nrepo link 134 138 --type related --note "Both involve browser APIs" # Make 155 a child of 134 — the source is the child nrepo link 155 134 --type parent # Mark a dependency: 160 blocks 134 nrepo link 160 134 --type blocks --note "Need auth system before building extension" ``` Every argument is the database `id`, not the `#number` a listing prints. See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). ### From Claude via MCP > **You:** Link the browser extension idea to the code snippets one as related. Also make the content-script idea a child of the extension. > > **Claude:** *(calls `link_ideas` twice)* Done — linked them as related, and set the content-script idea as a child of the extension idea. `link_ideas` also takes a `links` array for up to 50 relations in one call, returning a per-link result so one bad pair does not abort the batch. ### From the API ```bash curl -X POST https://neuralrepo.com/api/v1/map/relations \ -H "X-API-Key: nrp_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "source_idea_id": 134, "target_idea_id": 138, "relation_type": "related", "note": "Both involve browser APIs" }' ``` ### What the System Creates for You Two kinds of relation appear without you asking: - **`related`**, whenever a newly embedded idea scores above your related threshold (default 0.55) against an existing one. - **`parent`**, when you branch an idea or save one with a `parent_id` — created by the background queue a few seconds after the idea itself. Both are stored with `created_by: 'system'`, and the uniqueness constraint covers `(source, target, created_by)` — so a manual relation and a system one can coexist between the same pair, and a second *manual* link between them fails with `Relation already exists`. **Caution:** Editing an idea's title or body **deletes its system-created relations and re-derives them** from the new embedding. Manual relations survive; auto-detected ones can disappear if the edit moved the idea's meaning. ## Viewing the Graph ### Terminal: nrepo graph The `nrepo graph` command renders an idea's neighborhood as an ASCII tree, following relations in **both** directions and labelling each branch with the relation type that produced it: ```bash nrepo graph 134 --depth 2 ``` ``` #42 Browser extension for idea capture [building] ├── parent → #55 Content script for text selection [exploring] ├── parent → #56 Popup UI for saving highlighted text [captured] ├── related → #38 Chrome plugin for code snippets [exploring] └── blocks → #60 Authentication system [building] └── parent → #61 OAuth flow implementation [building] ``` The default depth is **1**. Raise it with `--depth`, up to 5 — but the text tree only ever draws **three levels**, so hops beyond the second exist solely in `--json`. Narrow a wide graph with `--type blocks` or `--type parent,supersedes`. ### Web App: Mind Map The web dashboard includes an interactive mind map visualization at **Organize > Mind Map**. It renders your entire idea graph as a force-directed layout where you can: - Click any node to view the idea - Drag nodes to rearrange the layout (Pro) - Filter by tag, status, or relation type - Zoom and pan to explore large graphs Free-tier users get view-only access — everything above except editing interactions like dragging and link creation. **Tip:** The mind map is most useful when you have 10+ linked ideas. Start building relations early and the graph becomes increasingly valuable over time. ## Building Connected Networks ### Pattern 1: Project Breakdown Start with a high-level idea and decompose it into children: ``` #42 Browser extension ├── #55 Content script ├── #56 Popup UI ├── #57 Service worker └── #58 Options page ``` Each child can be worked on independently and tracked with its own status. ### Pattern 2: Inspiration Chain Track how ideas evolve over time: ``` #10 "Simple bookmarking tool" └── [inspires] #25 "Smart bookmarks with AI tags" └── [inspires] #42 "Browser extension for idea capture" ``` ### Pattern 3: Dependency Graph Map out what blocks what: ``` #60 Auth system ──[blocks]──► #42 Browser extension #62 API rate limiter ──[blocks]──► #42 Browser extension ``` This tells you that #42 cannot ship until #60 and #62 are complete. ### Pattern 4: Supersession When an idea evolves so much that it replaces an older one: ```bash nrepo link 134 110 --type supersedes --note "Extension approach replaces the simple bookmarking idea" nrepo move 110 shelved ``` `supersedes` is the one type whose cycles are only **soft**-blocked: `nrepo link --force` will push a supersedes cycle through. `blocks` and `parent` cycles are refused outright, and no flag bypasses them — a refused cycle comes back as a `400`, not a `409`. ## Practical Example: Building an Ecosystem Imagine you are building a suite of developer tools. Here is how the graph might look: ``` #1 Developer Tools Suite (exploring) ├── [parent] #10 CLI Framework (building) │ ├── [parent] #11 Plugin system │ └── [parent] #12 Config file parser ├── [parent] #20 VS Code Extension (captured) │ └── [related] #10 CLI Framework ├── [parent] #30 GitHub Bot (exploring) │ ├── [blocks] #10 CLI Framework │ └── [related] #35 Webhook handler library └── [inspires] #40 SaaS Dashboard (captured) ``` This graph tells you: - The CLI Framework (#10) is the foundation — it blocks the GitHub Bot and relates to the VS Code Extension. - The SaaS Dashboard was inspired by the suite idea but is a separate effort. - You should focus on #10 first since other ideas depend on it. The tree shows the shape you end up with, not the order of arguments that built it: each `parent` edge above was created from the **child**, as `nrepo link <child-id> <parent-id> --type parent`. ## Removing Relations ```bash # List all relations for an idea nrepo links 134 # Remove a relation by specifying both idea IDs nrepo unlink <source-id> <target-id> ``` `nrepo unlink` finds the relation between the pair in **either** direction, so the argument order does not have to match how you created it. Via MCP, ask Claude to unlink the two ideas — the `unlink_ideas` tool takes a `relation_id`, which `get_idea` with `include_context: true` returns. **Caution:** Removing a relation always reports success, whether or not one existed. If you expected a link to disappear from the graph and it is still there, you probably removed a different pair — list them with `nrepo links` first. ## Next Steps - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): The relation model in depth — types, direction, and cycle rules. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): Explore the interactive mind map visualization in the web app. - [Idea to Project](https://docs.neuralconfig.com/neuralrepo/guides/idea-to-project/): Use the graph to plan and execute a project from idea to shipped. - [Relations & Graph API](https://docs.neuralconfig.com/neuralrepo/api/relations/): REST endpoints for creating, updating, and traversing relations. --- # Idea to Project Source: https://docs.neuralconfig.com/neuralrepo/guides/idea-to-project/ Every shipped project starts as a fleeting thought. This guide walks through NeuralRepo's recommended workflow for taking an idea from initial capture to a completed project. ## The Idea Lifecycle NeuralRepo uses five statuses that map to natural project phases: | Status | Phase | What Happens Here | |--------|-------|-------------------| | `captured` | Inbox | Raw idea, no evaluation yet. Just get it down. | | `exploring` | Research | Investigate feasibility. Add notes, links, and related ideas. | | `building` | Active development | You are writing code, designing, or creating. | | `shipped` | Done | The project is live, published, or complete. | | `shelved` | On hold | Not abandoned, but not active. Revisit later. | ## Step-by-Step Workflow 1. **Capture the idea.** Use whatever method is most convenient — Siri, Claude, CLI, web app, or email. Do not overthink it at this stage. The goal is to not lose the thought. ```bash nrepo push "Browser extension that highlights text and saves to NeuralRepo" ``` The idea is created with `captured` status. 2. **Triage and tag.** Periodically review your `captured` ideas. Add tags, flesh out the body, and decide whether to explore further or shelve. ```bash nrepo tag 134 chrome extension mvp nrepo edit 134 --body "Should integrate with the Share Sheet on desktop browsers..." ``` **Caution:** Editing the body re-embeds the idea and **re-derives its auto-detected relations and duplicate flags** from the new text. Manual links survive; system-created ones may change. 3. **Move to exploring.** When you decide to investigate an idea, update its status: ```bash nrepo move 134 exploring ``` During exploration, research feasibility, check for prior art, and add notes. Use the body field as a living document. 4. **Break it down with relations.** Large ideas benefit from sub-ideas. Create child ideas for specific components, then link each one *to* the umbrella idea — the source of a `parent` relation is the **child**: ```bash nrepo push "Content script for text selection" # Then link as child — child first, parent second: nrepo link 143 134 --type parent nrepo push "Popup UI for saving highlighted text" nrepo link 144 134 --type parent nrepo push "Background service worker for API calls" nrepo link 145 134 --type parent ``` These appear as children of the umbrella idea, creating a natural work breakdown structure. **Note:** Linking is a **Pro** feature, and every id above is the database `id` rather than the `#number` a listing shows. See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). 5. **Branch for variants.** If you are considering multiple approaches, use the `branch` command to create variants: ```bash nrepo branch 134 --title "Browser extension — Chrome-only MVP" nrepo branch 134 --title "Browser extension — cross-browser with WebExtension API" ``` Each branch is a new idea whose `parent_id` points at the original. The visible `parent` relation is created by the background queue a few seconds later, so it will not be in the response you get back — check with `nrepo links` if you need to confirm it landed. 6. **Generate an AI spec.** When you are ready to build, use the `develop` endpoint to generate a full project specification from your idea and its relations: ```bash curl -X POST https://neuralrepo.com/api/v1/ideas/134/develop \ -H "X-API-Key: nrp_your_key_here" ``` This requires a [BYOK AI key](https://docs.neuralconfig.com/neuralrepo/integrations/byok/), which is Pro-only. The AI reads the idea, its children, and related ideas to produce a structured spec with architecture recommendations, milestones, and open questions. **Caution:** **`develop` writes.** The generated spec is appended to the idea's own `body`, not returned as a separate document — so running it twice appends twice. Keep the original body short if you plan to use it. 7. **Pull to your local project.** Bring the idea and its spec into your working directory: ```bash nrepo pull 134 --to ./my-extension ``` This creates a markdown file with the full idea context — title, body (including the generated spec, since it lives in the body), tags, and related ideas. 8. **Build it.** Move to `building` status and start working: ```bash nrepo move 134 building ``` As you build, update the idea body with progress notes, decisions, and links to the actual code repository. 9. **Ship it.** When the project is live: ```bash nrepo move 134 shipped ``` Add a `source_url` pointing to the live project, repository, or release page. ## Using Tags for Phases Beyond status, tags help you track cross-cutting concerns: - `mvp` — minimum viable version of the idea - `v2` — future improvements after the initial ship - `blocked` — waiting on something external - `needs-research` — requires investigation before building - `weekend-project` — scope fits a weekend **Tip:** Combine status filters with tag filters to create focused views. For example, filter by `building` status and `mvp` tag to see only the active work you are shipping next. ## Example: From Idea to Shipped ``` Day 1: nrepo push "Habit tracker with streaks" → captured (id 155) Day 3: nrepo move 155 exploring → exploring Day 3: nrepo tag 155 mobile react-native mvp Day 5: nrepo push "Streak calculation algorithm" → id 156 Day 5: nrepo link 156 155 --type parent → 156 is a child of 155 Day 5: nrepo push "Push notification reminders" → id 157 Day 5: nrepo link 157 155 --type parent Day 7: POST /ideas/155/develop → spec appended to the body Day 7: nrepo pull 155 --to ./habit-tracker Day 8: nrepo move 155 building → building Day 14: nrepo move 155 shipped → shipped ``` ## Next Steps - [Building an Idea Graph](https://docs.neuralconfig.com/neuralrepo/guides/idea-graph/): Connect ideas into a rich network of relations for better organization. - [Claude Code Workflow](https://docs.neuralconfig.com/neuralrepo/guides/claude-code-workflow/): Use Claude Code alongside NeuralRepo for AI-assisted development. - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): What each status means and how statuses drive stale detection. - [nrepo branch](https://docs.neuralconfig.com/neuralrepo/cli/branch/): Fork an idea into a variant, and what the queue does afterwards. --- # Migration Source: https://docs.neuralconfig.com/neuralrepo/guides/migration/ If you have ideas scattered across Notion databases, Obsidian vaults, or other tools, NeuralRepo makes it straightforward to bring them all into one place. **Caution:** **Check two Free-plan ceilings before you start a bulk import.** A Free account stops accepting new ideas at **50 unarchived ideas** — `POST /ideas` returns `403 idea_limit_reached`, and there is no way to delete your way back under the cap that recovers the imported ideas. Free is also capped at **100 API requests per day**, so an import of a few hundred rows needs Pro or several days. The CLI path has the same limits: `nrepo push` is the same endpoint. ## From Notion 1. **Export from Notion.** Open your Notion database, click the `...` menu, and select **Export**. Choose **CSV** or **Markdown & CSV** format. Unzip the downloaded file. 2. **Map fields.** Notion properties map to NeuralRepo fields as follows: | Notion Property | NeuralRepo Field | Notes | |----------------|-----------------|-------| | Name / Title | `title` | Required | | Notes / Content | `body` | Supports markdown | | Tags (multi-select) | `tags` | Array of strings | | Status (select) | `status` | Map to: `captured`, `exploring`, `building`, `shipped`, `shelved` | | URL | `source_url` | Optional | 3. **Import via API.** Use a script to read the CSV and push each row: ```bash #!/bin/bash API_KEY="nrp_your_key_here" API_URL="https://neuralrepo.com/api/v1/ideas" # Skip header line, read CSV tail -n +2 exported.csv | while IFS=',' read -r title body tags status; do # Convert comma-separated tags to JSON array tags_json=$(echo "$tags" | sed 's/;/","/g' | sed 's/^/["/' | sed 's/$/"]/') curl -s -X POST "$API_URL" \ -H "X-API-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"title\": \"$title\", \"body\": \"$body\", \"tags\": $tags_json, \"status\": \"captured\" }" echo " -> Imported: $title" done ``` **Caution:** For large exports (100+ ideas), add a short delay between API calls to avoid rate limiting. Insert `sleep 0.5` after each curl command. ## From Obsidian Obsidian vaults are folders of markdown files, which makes migration straightforward. 1. **Identify your ideas folder.** If your vault has a specific folder for ideas (e.g., `Ideas/` or `Inbox/`), use that. Otherwise, you can import the entire vault. 2. **Push each file via CLI.** ```bash for file in ~/obsidian-vault/Ideas/*.md; do title=$(head -1 "$file" | sed 's/^# //') body=$(cat "$file") nrepo push "$title" --body "$body" --tag obsidian --tag imported done ``` 3. **Handle frontmatter.** If your Obsidian notes use YAML frontmatter with tags, you can extract them: ```bash for file in ~/obsidian-vault/Ideas/*.md; do title=$(head -1 "$file" | sed 's/^# //') body=$(cat "$file") # Extract tags from frontmatter (assumes format: tags: [tag1, tag2]) tags=$(grep '^tags:' "$file" | sed 's/tags: \[//' | sed 's/\]//' | tr -d ' ') if [ -n "$tags" ]; then IFS=',' read -ra tag_array <<< "$tags" tag_flags="" for t in "${tag_array[@]}"; do tag_flags="$tag_flags --tag $t"; done nrepo push "$title" --body "$body" $tag_flags else nrepo push "$title" --body "$body" --tag obsidian fi done ``` ## From Plain Text or JSON If you have ideas in a JSON file, you can import them directly: ```json [ { "title": "Build a habit tracker", "body": "Mobile app with streak counting and push notifications", "tags": ["mobile", "react-native"] }, { "title": "API rate limiter library", "body": "Token bucket implementation for Express middleware", "tags": ["api", "node"] } ] ``` ```bash #!/bin/bash API_KEY="nrp_your_key_here" # Use jq to iterate over the array jq -c '.[]' ideas.json | while read -r idea; do curl -s -X POST "https://neuralrepo.com/api/v1/ideas" \ -H "X-API-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d "$idea" echo " -> Imported: $(echo "$idea" | jq -r '.title')" sleep 0.3 done ``` ## Using the CLI for Bulk Import The nrepo CLI supports pushing from files directly: ```bash # Single idea from a file nrepo push "My idea" --body "$(cat ./idea.md)" --tag imported # Batch import with a loop for file in ./ideas/*.md; do filename=$(basename "$file" .md) nrepo push "$filename" --body "$(cat "$file")" --tag imported done ``` **Tip:** After importing, run a search for common terms to verify your ideas are indexed. NeuralRepo processes embeddings asynchronously, so semantic search results may take a few seconds to appear for newly imported ideas. ## What Happens to Imported Ideas Each import lands in D1 immediately and then goes through the background pipeline, so a few things appear on their own a few seconds later: - **Auto-tags**, on any idea you imported with **zero** tags. Pro gets AI-suggested tags drawn from your existing vocabulary; Free borrows tags shared by at least two similar neighbours. Import with tags and neither runs. - **`related` relations**, between any pair scoring above your related threshold (default 0.55). - **Duplicate detections**, above your dedup threshold (default 0.75). A bulk import of overlapping notes is exactly the situation that generates a lot of these. Both thresholds are yours to move, in **Settings ▸ Search** or via `search_threshold`, `dedup_threshold`, and `related_threshold` — each clamped to 0.1–0.9. Raising `dedup_threshold` before a messy import keeps the duplicate queue manageable. ## Post-Migration Checklist After importing, take these steps to organize your new repository: - **Review tags.** Consolidate similar tags (e.g., `js` and `javascript`) using the web dashboard. - **Set statuses.** Imported ideas default to `captured`. Move active ones to `exploring` or `building`. Anything left in `captured` past your stale threshold will start showing up in nudge emails and the review queue. - **Create relations.** Link related ideas together (Pro). The mind map in the web app helps visualize clusters. - **Check for duplicates.** Detection runs automatically on import, but **reviewing the results is Pro** — on Free, `GET /ideas/duplicates` always returns an empty list. - **Leave the junk alone until you are sure.** `DELETE /ideas/:id` and `nrepo rm` set `is_archived = 1` and delete the search vector. You can bring an idea back from the **Archive** view or with `POST /ideas/:id/restore`, but archived ideas are excluded from every export, so an export taken afterwards will not contain them, and a permanent delete can't be undone. ## Next Steps - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): Organize your imported ideas with a consistent tagging system. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): Tune the thresholds and work through what a bulk import surfaces. - [Semantic Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): Learn how NeuralRepo indexes and searches your ideas. - [Exporting Your Ideas](https://docs.neuralconfig.com/neuralrepo/integrations/export/): The reverse trip — JSON, CSV, or an Obsidian-ready Markdown vault. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): The idea cap, request budget, and which review tools need Pro. --- # Apple Watch Source: https://docs.neuralconfig.com/neuralrepo/integrations/apple-watch/ NeuralRepo works on Apple Watch through **Siri Shortcuts**. There is no separate watch app to install — the Quick Capture shortcut you add on your iPhone runs natively on your wrist. ## How It Works Apple Watch runs Siri Shortcuts directly on the device starting with watchOS 9. When you trigger a NeuralRepo shortcut, the watch makes the API call itself (or routes through your paired iPhone if needed). The experience is simple: 1. Raise your wrist or press the Digital Crown. 2. Say **"Hey Siri, NeuralRepo"**. 3. Dictate your idea. 4. Siri confirms the idea was saved. The idea appears in your NeuralRepo dashboard within seconds, tagged with a `captured` status and ready for you to expand on later. ## Requirements | Requirement | Minimum | |-------------|---------| | watchOS | 9.0 or later | | iPhone | Paired iPhone with Siri Shortcuts configured | | NeuralRepo plan | **Pro** (the Apple Shortcuts card and API key creation both require Pro) | | API key | Required — generated on the **Settings ▸ Integrations ▸ Apple Shortcuts** card, or in **Settings ▸ Connections** | ## Setup 1. **Add the shortcut on your iPhone.** Follow the [Siri Shortcuts setup guide](https://docs.neuralconfig.com/neuralrepo/integrations/siri-shortcuts/) to add Quick Capture from **Settings ▸ Integrations** and paste your API key. 2. **Verify sync to watch.** Open the **Watch** app on your iPhone, scroll to **Siri**, and confirm the NeuralRepo shortcut is listed. Quick Capture is an iCloud shortcut, so it propagates to every device on your Apple account. 3. **Test from your wrist.** Raise your wrist and say "Hey Siri, NeuralRepo" to test the Quick Capture shortcut. **Tip:** For fastest access, add the Quick Capture shortcut as a **complication** on your watch face. Tap the complication to launch the shortcut instantly without using Siri. ## What You Can Capture Quick Capture is the only NeuralRepo shortcut, and the watch is exactly what it was built for. It captures a single dictated idea with: - **Title** — your spoken words, transcribed by Siri - **Status** — `captured` (default) - **Source** — recorded as `siri`, so you can filter for wrist captures later For richer input — sharing a URL or a text selection from another app — share it to **Mail** on iPhone or iPad and send it to your personal [email capture](https://docs.neuralconfig.com/neuralrepo/capture/email/) address. ## Tips for Watch Capture - **Keep ideas short.** The watch is best for capturing a quick title or seed thought. Expand the idea later from the web app or CLI. - **Speak clearly.** Background noise affects dictation accuracy. A brief pause before speaking helps Siri detect the start of your idea. - **Batch capture.** If multiple ideas hit at once, run the shortcut repeatedly. Each call creates a separate idea. - **Review later.** After a capture session, open the NeuralRepo dashboard and filter by `captured` status to review and flesh out your watch-captured ideas. ## Limitations - **No browsing.** You cannot search or view existing ideas from the watch. Use the web app, CLI, or Claude for that. - **Dictation only.** There is no keyboard input on the watch for these shortcuts. If Siri mishears you, re-run the shortcut. - **Network required.** The watch must have connectivity (Wi-Fi or cellular) to reach the NeuralRepo API. **Note:** If you have an Apple Watch Ultra or SE with cellular, you can capture ideas even when your iPhone is not nearby. ## Next Steps - [Siri Shortcuts Setup](https://docs.neuralconfig.com/neuralrepo/integrations/siri-shortcuts/): Add the Quick Capture shortcut and configure its API key. - [Capture with Siri](https://docs.neuralconfig.com/neuralrepo/capture/siri/): What a voice capture creates, and where it lands. - [Email Capture](https://docs.neuralconfig.com/neuralrepo/capture/email/): Share links and selections from iPhone by mailing them to your personal address. - [Quick Start](https://docs.neuralconfig.com/neuralrepo/getting-started/quickstart/): Get started with NeuralRepo in under 5 minutes. --- # BYOK AI Providers Source: https://docs.neuralconfig.com/neuralrepo/integrations/byok/ NeuralRepo's Anthropic-powered features run on **Bring Your Own Key (BYOK)**. You connect your own API key and NeuralRepo uses it for the agent's Claude models, AI-generated specs, and the weekly digest's written summary. **Note:** **BYOK is a Pro feature.** The **Settings ▸ AI** tab is Pro-only, and `PUT /user/byok/:provider` and the test endpoint both return `403` on Free. (`DELETE` is not gated — a downgraded account can still remove a key it already stored.) ## What BYOK Enables | Feature | Description | |---------|-------------| | **AI agent — Claude models** | Use Sonnet, Haiku, or Opus in the Agent view and the mind map chat panel. The two Workers AI models need no key. | | **AI-Generated Specs** | `POST /ideas/:id/develop` uses your key to generate a project specification and appends it to the idea's body. | | **Weekly Digest** | Your Sunday digest is an AI-written narrative of the week's activity — new ideas, pending duplicates, and stale ideas — with a specific suggestion. Without a key you still get the digest, as a stats summary. | Core functionality — capture, search, organize, graph, and the agent itself on a Workers AI model — works with no AI key at all. ## Agent Model Registry The agent offers five models. The three Anthropic models require an Anthropic BYOK key; the two Workers AI models run on Cloudflare's own inference and need no key. | Model | Provider | Requires BYOK | Context | Max steps | Notes | |-------|----------|:-------------:|---------|:---------:|-------| | GPT-OSS 120B (default) | Workers AI | No | 128K | 12 | `@cf/openai/gpt-oss-120b`; runs in code mode by default | | Qwen 30B | Workers AI | No | 32K | 6 | `@cf/qwen/qwen3-30b-a3b-fp8`; compact system prompt | | Sonnet | Anthropic | Yes | 1M | 10 | Newest Sonnet; never older than `claude-sonnet-5` | | Haiku | Anthropic | Yes | 200K | 8 | Newest Haiku; never older than `claude-haiku-4-5` | | Opus | Anthropic | Yes | 1M | 10 | Newest Opus; never older than `claude-opus-5` | The Anthropic rows name a **tier**, not a fixed model ID. When a Claude model is used, NeuralRepo lists Anthropic's models (`GET /v1/models`) with an Anthropic key and picks the newest Haiku, Sonnet and Opus. That choice is cached for 24 hours, so a new Claude release is picked up within a day without an app update. If the lookup fails for any reason — an error status, a timeout, an empty list — the tier falls back to its floor: `claude-haiku-4-5`, `claude-sonnet-5` or `claude-opus-5`. Where Anthropic offers both an undated alias and a dated snapshot of the same model, the alias is chosen. The Context column is the budget the agent works to, not a promise about every model in the tier. Only a budget below 200K causes history trimming (see [Conversation History](https://docs.neuralconfig.com/neuralrepo/agent/overview/#conversation-history)). **Caution:** **Picking a Claude model without an Anthropic key fails — it does not fall back.** The agent replies "Anthropic API key required for Claude models. Select a Workers AI model from the dropdown or add a key in Settings." An OpenAI or OpenRouter key does not satisfy this: the agent's Claude models resolve specifically to Anthropic. All Anthropic requests route through **Cloudflare AI Gateway** — your key is never exposed beyond Cloudflare's infrastructure. ## Supported BYOK Providers Three providers can be stored. Anthropic is the one the agent's Claude models use; all three are eligible for the digest summary and the develop endpoint, which pick a provider by the priority order below. Only an Anthropic key gets the newest-model lookup; the OpenAI and OpenRouter defaults are fixed IDs. | Provider | Key format | Default model used | |----------|------------|--------------------| | **Anthropic** | `sk-ant-…` | The newest Sonnet (falls back to `claude-sonnet-5`) | | **OpenAI** | `sk-…` | `gpt-4o` | | **OpenRouter** | `sk-or-…` | `anthropic/claude-sonnet-5` (fixed; not looked up) | ## Setup **Web App** 1. Go to **Settings ▸ AI** in the NeuralRepo web dashboard. The panel is headed **AI Providers**. 2. Find the card for your provider (Anthropic, OpenAI, or OpenRouter). 3. Paste your API key. The **Save** button stays disabled until the key is at least 10 characters. 4. Click **Save**. The key is encrypted and stored, and the card switches to a **Configured** badge. 5. Click **Test connection** to verify the key works. A green **Connected** confirms it. **API** Use the BYOK API endpoints to manage keys programmatically: ```bash # Set a provider key (Pro) curl -X PUT https://neuralrepo.com/api/v1/user/byok/anthropic \ -H "X-API-Key: nrp_your_key_here" \ -H "Content-Type: application/json" \ -d '{"api_key": "sk-ant-your-anthropic-key"}' ``` ```bash # Test the key (Pro) curl -X POST https://neuralrepo.com/api/v1/user/byok/anthropic/test \ -H "X-API-Key: nrp_your_key_here" ``` Replace `anthropic` with `openai` or `openrouter` for other providers. Any other value returns `400`. ## Provider Priority When NeuralRepo needs an AI call for a digest or a spec, it selects a provider in this order: 1. **Your explicit preference**, if you have set one. 2. **Anthropic**, if a key is configured. 3. **OpenAI**, if a key is configured. 4. **OpenRouter**, if a key is configured. The **Preferred provider** dropdown only appears once you have **more than one** provider configured — with a single key there is nothing to choose between, and the default `Auto` option is the Anthropic → OpenAI → OpenRouter order above. If no key is available for any provider, the AI feature is skipped gracefully: the weekly digest is sent as a stats summary instead of a narrative. ## Security Your API keys are protected with multiple layers: - **Encrypted at rest.** Keys are encrypted with **AES-256-GCM** using a key derived from the server-side `ENCRYPTION_KEY` secret via HKDF-SHA256, with a fresh 12-byte IV per encryption. The plaintext key is never stored. - **AI Gateway routing.** All AI requests are routed through Cloudflare's AI Gateway, which provides logging, rate limiting, and caching without exposing your key to additional third parties. - **No key exposure.** The API never returns your key, in full or in part. `GET /user/byok/status` reports only which providers are configured. **Caution:** NeuralRepo never shares your API key with anyone other than the selected AI provider (via AI Gateway). However, usage of your key by NeuralRepo counts against your provider's billing. Monitor your provider dashboard for unexpected usage. ## Managing Keys ```bash # Which providers are configured, and which one is active curl https://neuralrepo.com/api/v1/user/byok/status \ -H "X-API-Key: nrp_your_key_here" ``` ```json { "active_provider": "anthropic", "has_anthropic_key": true, "has_openai_key": false, "has_openrouter_key": false } ``` ```bash # Remove a provider key (not Pro-gated) curl -X DELETE https://neuralrepo.com/api/v1/user/byok/openai \ -H "X-API-Key: nrp_your_key_here" ``` ## Testing Your Key Before relying on AI features, verify your key works: ```bash curl -X POST https://neuralrepo.com/api/v1/user/byok/anthropic/test \ -H "X-API-Key: nrp_your_key_here" ``` **Success response:** ```json { "ok": true } ``` **Failure response:** ```json { "ok": false, "error": "Invalid API key" } ``` **Caution:** A failed test still returns **HTTP 200**. Check the `ok` field, not the status code — `res.ok` in a fetch client will be `true` for a key that does not work. See [Failures that are not error statuses](https://docs.neuralconfig.com/neuralrepo/api/errors/). ## Next Steps - [AI Agent Overview](https://docs.neuralconfig.com/neuralrepo/agent/overview/): Model picker, code mode, and what the agent can do with your ideas. - [Weekly Digest](https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/): What the digest contains with and without a key, and how to opt out. - [User & Settings API](https://docs.neuralconfig.com/neuralrepo/api/user/): The full BYOK endpoint reference alongside the rest of the user surface. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): What Pro unlocks, including the AI settings tab. --- # Claude Code Source: https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/ Claude Code is Anthropic's AI coding agent that runs in your terminal. The recommended way to connect NeuralRepo is the **MCP remote connector** — one connector that works everywhere: Claude.ai, Claude Desktop, mobile, and Claude Code. The **nrepo CLI** is an optional complement for terminal-native workflows. ## Method 1: MCP Remote Connector The MCP connector gives Claude Code direct access to your ideas. The `/mcp/v2` address below has 22 discrete tools, listed on [Claude Connector (MCP v2)](https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/). The original `https://neuralrepo.com/mcp/` keeps working with no end date; it has nine tools — save, search, update, delete, get, list tags, link, unlink, and code mode — and is the one to use if you rely on `code_mode`. ### Setup ```bash claude mcp add --transport http neuralrepo https://neuralrepo.com/mcp/v2 ``` On your next Claude Code session, NeuralRepo tools are available. The first time Claude uses a tool, you will be prompted to authorize via OAuth. Verify the connection: ```bash claude mcp list ``` You should see `neuralrepo` listed with a status of `connected` or `pending auth`. ### Using MCP in Claude Code Once connected, you can ask Claude Code to interact with your ideas naturally: - "Search my NeuralRepo for ideas about authentication" - "Save this project concept as an idea tagged with `api` and `v2`" - "Get the auth idea with full context and use it as the spec for this project" - "Move that idea to `building`" **Tip:** When starting a new project, ask Claude Code to search your NeuralRepo first. It often surfaces ideas and context you captured weeks ago that are directly relevant to what you are building now. **Caution:** **Let Claude carry the identifier; don't retype the number you see.** MCP tools key off the database `id`, while listings — in the CLI, the web app, and Claude's own summaries — usually show the display `#number`. They are the same only on the very first account. Say "that idea" rather than "idea 42". See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). On the original `/mcp/` connector, two of the nine tools are Pro-only: `link_ideas` and `unlink_ideas`. Everything else — saving, searching, updating, deleting, listing tags, and `code_mode` — works on Free. For `/mcp/v2`, see the plan table on [Claude Connector (MCP v2)](https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/). ## Method 2: nrepo CLI The CLI provides direct command-line access to your NeuralRepo, which Claude Code can invoke as shell commands. ### Setup 1. Install the CLI globally: ```bash npm install -g @neuralconfig/nrepo ``` 2. Authenticate: ```bash nrepo login ``` This opens a browser window and signs you in **through GitHub**, matching your account by verified primary email. What it stores is not a session token: it mints a real `nrp_` API key labelled `CLI (auto-generated)` and writes it to `~/.config/neuralrepo/config.json`. That path has no plan check, which is why the CLI works on Free. 3. **(Optional)** Install the NeuralRepo skill for Claude Code: ```bash nrepo install-skill ``` This copies the skill to `~/.claude/skills/neuralrepo/SKILL.md`. If `~/.claude` does not exist the command refuses and exits `1` — install Claude Code first. **Tip:** CLI output is human-readable by default on both login methods. Pass `--json` to any command for machine-parseable output in scripts, and `--human` to force text back on — `--human` wins when both are given. ### Key Commands for Claude Code Workflows ```bash # Search ideas nrepo search "authentication flow" # Pull an idea into the current project directory nrepo pull 134 --to ./ # Push a new idea from a file nrepo push "API redesign" --body "$(cat ./notes.md)" --tag api --tag v2 # Update status nrepo move 134 building # View the idea graph nrepo graph 134 ``` Every `<id>` above is the database `id`, not the `#number` in a listing — see [CLI troubleshooting](https://docs.neuralconfig.com/neuralrepo/cli/troubleshooting/) if a command reports `Idea not found` for an idea you can plainly see. ## Recommended Workflow The most productive pattern combines both methods: 1. **Search for context.** Ask Claude Code to search NeuralRepo via MCP: "Do I have any ideas about a CLI dashboard?" 2. **Pull the idea locally.** Use the CLI to bring the full idea into your project: ```bash nrepo pull 134 --to ./my-project ``` This creates a markdown file with the idea title, body, tags, and related ideas. The `.neuralrepo` file it writes alongside is never read back — there is no sync in the other direction. 3. **Build with context.** Claude Code can read the pulled file and use it as a specification for implementation. 4. **Update status.** When you start building, update the idea: ```bash nrepo move 134 building ``` 5. **Ship and close.** When deployed, mark it done: ```bash nrepo move 134 shipped ``` ## Example Conversation ``` You: Search my NeuralRepo for ideas about a REST API rate limiter Claude Code: [calls search_ideas with query "REST API rate limiter"] I found 2 relevant ideas: - #38 "Token bucket rate limiter for API" (exploring) — id 134 - #41 "Sliding window rate limit with Redis" (captured) — id 137 You: Pull the token bucket one into this project Claude Code: [runs: nrepo pull 134 --to ./] Done. Created ./idea-134-token-bucket-rate-limiter.md You: Read that file and implement the rate limiter based on the spec Claude Code: [reads the file, implements the rate limiter] I've implemented a token bucket rate limiter based on your idea spec... You: Move it to building Claude Code: [calls update_idea with id=134, status="building"] Updated "Token bucket rate limiter for API" to "building". ``` Note the two numbers in the search result. `#38` is the display number the web app and the CLI print; `134` is the database `id` every command and tool actually takes. Asking for "the token bucket one" lets Claude use the right one. ## The NeuralRepo Skill `nrepo install-skill` installs a Claude Code skill named `neuralrepo`, invocable as `/neuralrepo` with a search query or idea title as its argument. Its instructions teach Claude the CLI's core patterns — search before pushing, pass `--json` when parsing output, and how to move ideas through statuses: ``` /neuralrepo changelog generator ``` **Note:** **The skill drives the CLI, not the MCP connector.** Its `allowed-tools` is `Bash(nrepo *)`, so it needs the CLI installed and authenticated; it cannot call MCP tools. The two integrations complement each other but the skill is not a front-end for both. ## Next Steps - [MCP Server Reference](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): The original /mcp/ connector: all 9 tools with parameter tables and examples. - [Claude Code Workflow](https://docs.neuralconfig.com/neuralrepo/guides/claude-code-workflow/): The brainstorm-then-build pattern across Claude and Claude Code. - [Idea to Project Workflow](https://docs.neuralconfig.com/neuralrepo/guides/idea-to-project/): End-to-end guide for turning a captured idea into a shipped project. - [CLI Authentication](https://docs.neuralconfig.com/neuralrepo/cli/authentication/): Both login paths in detail, and what each one stores. --- # Claude Connector (MCP v2) Source: https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/ The NeuralRepo Claude connector is the MCP server at **`https://neuralrepo.com/mcp/v2`**. Enter that URL exactly, **without a trailing slash**. It gives Claude 22 separate tools for your ideas: 8 that only read, 4 that add something, and 10 that change or remove something. Each tool says which kind it is, so Claude can ask before it changes anything. ``` https://neuralrepo.com/mcp/v2 ``` ## Which endpoint should I use, v2 or v1? **Use v2** unless you depend on `code_mode`. The two endpoints run side by side, share the same OAuth sign-in and tokens, and work on the same library. | | `/mcp/v2` (this page) | `/mcp/` (v1) | |---|---|---| | URL to enter | `https://neuralrepo.com/mcp/v2` | `https://neuralrepo.com/mcp/` | | Tools | 22 discrete tools, each with a title, read-only/destructive hints and an output schema | 8 named tools plus `code_mode` | | `code_mode` (sandboxed JavaScript against the API) | No | Yes, only here | | Archive and restore | `archive_idea`, and `restore_idea` brings an idea back | `delete_idea` archives, no restore tool | | Merging, duplicates, tag clean-up, attached links, stats | Each is its own tool | Only through `code_mode` | | Listed in the Claude connector directory | Yes, this is the endpoint submitted | No | The v1 endpoint is unchanged and keeps working. Its reference is at [MCP Server for Claude](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/). ## How do I connect it? **Claude.ai** 1. Open [claude.ai](https://claude.ai) and go to **Settings ▸ Connectors**. 2. Click **Add custom connector**. 3. Enter the URL, with no slash at the end: ``` https://neuralrepo.com/mcp/v2 ``` 4. Connect. You are sent to NeuralRepo to sign in (GitHub, Google, Apple or a magic link) and approve access. The consent page shows the host Claude will return you to. **Claude Desktop and mobile** Add the connector once on claude.ai (see the first tab). Connectors belong to your Claude account, so the same connector then appears in Claude Desktop and the Claude mobile apps without setting it up again. **Claude Code** Run: ```bash claude mcp add --transport http neuralrepo https://neuralrepo.com/mcp/v2 ``` Claude Code opens the NeuralRepo sign-in the first time it needs the server. If you already added the v1 endpoint under the name `neuralrepo`, remove it first (`claude mcp remove neuralrepo`) or give this one another name. ### Why no trailing slash? Claude checks that the server's OAuth **protected-resource document** names exactly the URL you entered. NeuralRepo publishes that document for v2 at `https://neuralrepo.com/.well-known/oauth-protected-resource/mcp/v2`, and its `resource` value is `https://neuralrepo.com/mcp/v2`, with no slash. The server answers on both `/mcp/v2` and `/mcp/v2/`, but only the no-slash form matches, so `https://neuralrepo.com/mcp/v2/` can fail at sign-in. ## How does sign-in work? The connector uses the same OAuth server as v1: Dynamic Client Registration at `/mcp/register`, PKCE with `S256` required, and two scopes, **`ideas:read`** and **`ideas:write`**. Token lifetimes, revoking a connection, and the PKCE errors are described under [OAuth Flow](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/#oauth-flow) on the v1 page; they apply to v2 unchanged. Scopes map to tools like this: - The 7 **read-only** tools need `ideas:read` or `ideas:write`. - Every other tool needs `ideas:write`. With a read-only connection it returns: *"This connection has read-only access (ideas:read). Reconnect NeuralRepo with write access to make changes."* ## What can each plan do? Every tool is available on both plans, but some return less on Free. | Limit | Free | Pro | |---|---|---| | Active (unarchived) ideas | 50. `save_idea` and `restore_idea` refuse at the cap. | Unlimited | | Semantic search in `search_ideas` | 10 per calendar month, then keyword matching with a `notice` | Unlimited | | `link_ideas`, `unlink_ideas` | Refused: *"Linking ideas is a Pro-plan feature…"* | Yes | | `list_duplicates` | Empty list with a `notice` | Yes | | `resolve_duplicate` | Refused: *"Duplicate detection is a Pro-plan feature, so nothing was changed."* | Yes | | `get_idea` duplicates section | Always an empty list | Pending duplicates | | `get_stats` `pending_duplicates` | `null` | A count | | `get_review_queue` duplicate groups | Empty list with a `notice` | Yes | At the Free cap, `save_idea` returns an error: *"This library has reached the free plan's limit of 50 active ideas, so the idea was not saved. Archiving an idea makes room for a new one."* The monthly semantic-search allowance is one counter per account, shared with the v1 endpoint and the REST API's semantic search. `merge_ideas`, `add_link`, `remove_link` and the tag tools have no plan check. ## What do the tools return? Each tool returns **`structuredContent`** that matches the `outputSchema` it publishes in `tools/list`, plus the same JSON as one text block. When a tool fails, the result has **`isError: true`** and a sentence Claude can act on, for example: - `No idea with id 999 in this library.` - `Invalid arguments for update_idea: status: …` (the input failed validation) - `The result is too large (… characters). Ask for fewer items: lower the limit or narrow the filters.` (results over 100,000 characters) Every tool that takes an idea takes its database **`id`**, not the `#number` shown in the app. Results carry both: `id` and `number`. See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). ## Read-only tools These eight tools never change anything (`readOnlyHint: true`). ### search_ideas Finds ideas by meaning with a `query`, or lists them newest first without one. | Parameter | Type | Required | Notes | |---|---|---|---| | `query` | string | No | 1–500 characters. Omit to browse. | | `status` | string | No | `captured`, `exploring`, `building`, `shipped`, `shelved` | | `tag` | string | No | Only ideas with this tag (max 50 characters) | | `source` | string | No | `web`, `cli`, `claude-mcp`, `siri`, `email`, `api`, `shortcut`, `ios` | | `created_after` | string | No | ISO date (`2026-10-01`) or date-time; inclusive | | `created_before` | string | No | ISO date or date-time; exclusive | | `limit` | integer | No | 1–50, default 20 | | `cursor` | string | No | `next_cursor` from the previous page. Browsing only. | Returns `search_type` (`browse`, `semantic` or `keyword`), `ideas` (each with `id`, `number`, title, status, source, tags, a 280-character `body_preview`, dates, and `score` for semantic results), `total` (browsing only; `null` with a query) and `next_cursor`. A query returns one page; passing `cursor` with a query is an error. If semantic search finds nothing above your score threshold, or fails, the tool answers with keyword matches. ### get_idea Returns one idea with its full body. | Parameter | Type | Required | Notes | |---|---|---|---| | `id` | integer | Yes | Idea `id` | | `include` | string[] | No | Any of `relations`, `links`, `duplicates`. Default all three; `[]` for the idea alone. | Relations come with a `relation_id` (for `unlink_ideas`) and a `direction`; links come with an `id` (for `remove_link`); duplicates come with a `detection_id` (for `resolve_duplicate`). `get_idea` also finds **archived** ideas by id, with `archived: true`. ### get_idea_context Returns one idea with its full body plus its closest related ideas and attached links in one compact result. Meant for planning or writing a spec around an idea. | Parameter | Type | Required | Notes | |---|---|---|---| | `id` | integer | Yes | Idea `id` | | `max_related` | integer | No | 0–10, default 5 | Each related idea appears once, with all its `relation_types` merged, a score and a `body_preview`. ### list_tags Lists your tags with how many active ideas use each. | Parameter | Type | Required | Notes | |---|---|---|---| | `sort` | string | No | `count` (most used first, default) or `name` | | `min_count` | integer | No | Default 1, so unused tags are left out. Pass 0 to include them. | | `limit` | integer | No | 1–200, default 50 | | `cursor` | string | No | `next_cursor` from the previous page | ### find_similar_tags Finds tags that look like variants of one tag: the same word spelled differently ("Mind Map", "mind-maps") and tags with a close meaning. | Parameter | Type | Required | Notes | |---|---|---|---| | `tag` | string | Yes | An existing tag name | | `limit` | integer | No | 1–20, default 5 | Each match has `match: "spelling"` or `"meaning"`, an `idea_count`, and a `similarity` (null for spelling matches). If the tag has no meaning index yet, only spelling variants come back, with a `notice`. ### get_stats Counts for your library. No parameters. Returns `total` (active ideas), `by_status`, `recent_7d` (ideas created in the last 7 days), `pending_duplicates` (`null` on Free) and `top_tags` (up to 10). ### list_duplicates Lists ideas detected as likely duplicates, grouped so that ideas duplicating each other sit together, best match first. **Pro**; Free gets an empty list and a `notice`. | Parameter | Type | Required | Notes | |---|---|---|---| | `limit` | integer | No | Groups to return, 1–50, default 20 | Each group has its `ideas`, its `pairs` (each with a `detection_id`, `idea_id`, `duplicate_of_id` and `similarity`) and `max_similarity`. Detection runs in the background after an idea is saved; see [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/). ### get_review_queue Everything that needs a decision, in one compact response, for a weekly triage. Three sections: - **`stale`**: ideas still `captured` and not updated for your stale threshold (Settings, default 30 days), with `days_stale`. - **`connections`**: links created in the last 7 days, by NeuralRepo or by you, with both ideas' `id`, `number` and title. - **`duplicate_groups`**: the same groups as `list_duplicates`. **Pro**; Free gets an empty list and a `notice`. | Parameter | Type | Required | Notes | |---|---|---|---| | `section` | string | No | `all` (default), `stale`, `connections` or `duplicates` | | `limit` | integer | No | Items per section, 1–50, default 10 | | `offset` | integer | No | Items to skip in each returned section, default 0 | Bodies are cut to 160-character `body_preview`s. `counts` gives the totals across all pages, `has_more` says which sections have another page, and `stale_threshold_days` and `connection_window_days` echo the windows used. The ids work with `update_ideas` (e.g. shelve stale ideas), `resolve_duplicate`, `merge_ideas` and `get_idea`. ## Write tools that add These four tools add or bring back data and destroy nothing (`destructiveHint: false`). ### save_idea Saves a new idea. Ideas saved this way have `source: "claude-mcp"`. | Parameter | Type | Required | Notes | |---|---|---|---| | `title` | string | Yes | 1–200 characters | | `body` | string | No | Markdown, max 50,000 characters | | `tags` | string[] | No | Max 20. `"a, b"` is split into two tags. Stored lowercase with spaces as hyphens. | | `status` | string | No | Default `captured` | | `source_url` | string | No | `http` or `https` URL, max 2,000 characters | | `parent_id` | integer | No | Make it a sub-idea of this idea `id`; must exist | Returns the saved `idea`. Embedding, duplicate detection and auto-tagging run [in the background](https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/) afterwards, so any duplicate shows up later in `list_duplicates`, not in this result. Free accounts are refused at 50 active ideas. ### link_ideas Records a relationship between two ideas. **Pro.** | Parameter | Type | Required | Notes | |---|---|---|---| | `source_idea_id` | integer | Yes | | | `target_idea_id` | integer | Yes | Must differ from the source | | `relation_type` | string | No | `related` (default), `parent`, `blocks`, `inspires`, `supersedes` | | `note` | string | No | Max 500 characters | A `parent` or `blocks` link that would close a loop is refused, naming the loop. Linking the same pair again is refused; remove the old link with `unlink_ideas` first. **Caution:** For `parent`, NeuralRepo stores the **source as the child** of the target, which is how ideas saved with a `parent_id` are linked. The tool's own description says the opposite ("the source contains the target"). To make idea 96 the parent of idea 138, link `source_idea_id: 138`, `target_idea_id: 96`. See [Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/). ### add_link Attaches a web address to an idea. | Parameter | Type | Required | Notes | |---|---|---|---| | `idea_id` | integer | Yes | | | `url` | string | Yes | `http` or `https`, max 2,000 characters | | `title` | string | No | Max 200 characters | | `link_type` | string | No | `url` (default), `claude-chat`, `github-repo`, `github-issue` | ### restore_idea Brings an archived idea back into searches and lists, and queues it to be re-indexed for semantic search. | Parameter | Type | Required | Notes | |---|---|---|---| | `id` | integer | Yes | Idea `id`. `get_idea` finds archived ideas by id. | Restoring an idea that isn't archived changes nothing and succeeds. On Free, restoring is refused while the library holds 50 active ideas. ## Write tools that change or remove These ten tools overwrite or delete something (`destructiveHint: true`). Claude may ask you to confirm before calling them. ### update_idea Changes one idea's title, body, status or tags. | Parameter | Type | Required | Notes | |---|---|---|---| | `id` | integer | Yes | | | `title` | string | No | 1–200 characters | | `body` | string | No | Replaces the whole body; max 50,000 characters | | `status` | string | No | | | `tags` | string[] | No | Replaces every tag | | `add_tags` | string[] | No | Adds tags | | `remove_tags` | string[] | No | Removes tags | Give at least one change. `tags` can't be combined with `add_tags` or `remove_tags`, and an idea can hold at most 20 tags. Returns the `idea` and a `changes` object listing only the fields whose value changed (`from`/`to`, or `added`/`removed` for tags). ### update_ideas Sets the status and/or adds or removes tags on up to 50 ideas at once. Titles and bodies are not changed. | Parameter | Type | Required | Notes | |---|---|---|---| | `ids` | integer[] | Yes | 1–50 idea ids | | `status` | string | No | | | `add_tags` | string[] | No | | | `remove_tags` | string[] | No | | **Not all-or-nothing:** each idea succeeds or fails on its own. The result is `{ updated, errors, results }`, with one entry per idea, so check `errors` rather than assuming the whole batch applied. ### archive_idea Archives an idea: it leaves searches, lists and counts, its search vector is removed, and the idea itself is kept. `restore_idea` brings it back. | Parameter | Type | Required | Notes | |---|---|---|---| | `id` | integer | Yes | | On Free, archiving frees a slot under the 50-idea cap. ### unlink_ideas Deletes one relationship between two ideas. **Pro.** | Parameter | Type | Required | Notes | |---|---|---|---| | `relation_id` | integer | Yes | From the `relations` in `get_idea` | An id that doesn't exist returns an error (`No relation with id … in this library.`). ### remove_link Deletes one web address attached to an idea. | Parameter | Type | Required | Notes | |---|---|---|---| | `idea_id` | integer | Yes | | | `link_id` | integer | Yes | From the `links` in `get_idea` | ### resolve_duplicate Resolves one detected duplicate pair. **Pro.** | Parameter | Type | Required | Notes | |---|---|---|---| | `detection_id` | integer | Yes | From `list_duplicates` or `get_idea` | | `action` | string | Yes | `dismiss` (not duplicates; stop suggesting the pair) or `merge` | | `keep_id` | integer | No | `merge` only. Which of the pair to keep; default the pair's `idea_id`, the idea captured later. | | `merged_body` | string or null | No | `merge` only. See `merge_ideas`. | A merge works exactly like `merge_ideas`. A detection that is no longer pending is refused. ### merge_ideas Merges one idea into another. Available on both plans. | Parameter | Type | Required | Notes | |---|---|---|---| | `keep_id` | integer | Yes | The idea to keep | | `absorb_id` | integer | Yes | The idea to absorb and archive | | `merged_body` | string or null | No | The combined body. Omit to append the absorbed body under a "Merged from #N" heading; `null` leaves the kept idea with no body. | The kept idea gets the merged body, the absorbed idea's tags, links and relations, and the earlier of the two creation dates. The absorbed idea is **archived**, not deleted, so `restore_idea` can bring it back. Restoring does not undo the merge: the kept idea keeps the merged body and everything moved to it. Both ideas must be active; merging an archived idea is refused. ### rename_tag Renames a tag on every idea that has it. | Parameter | Type | Required | Notes | |---|---|---|---| | `tag` | string | Yes | The current name | | `new_name` | string | Yes | One tag name, stored lowercase with spaces as hyphens | If another tag already has the new name, the call is refused; use `merge_tags` instead. ### merge_tags Folds several tags into one: every idea with any of them gets the `into` tag, and the folded tags are removed. | Parameter | Type | Required | Notes | |---|---|---|---| | `tags` | string[] | Yes | 1–20 tag names to fold in | | `into` | string | Yes | The tag to keep; created if it doesn't exist | If any named tag doesn't exist, nothing is merged. ### delete_tag Deletes a tag and removes it from every idea. The ideas are kept. **This cannot be undone.** | Parameter | Type | Required | Notes | |---|---|---|---| | `tag` | string | Yes | | ## Next steps - [MCP Server for Claude (v1)](https://docs.neuralconfig.com/neuralrepo/integrations/mcp/): The original /mcp/ endpoint with code_mode, plus OAuth token lifetimes and revoking access. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): How duplicates are found after you save, and what merge and dismiss do. - [Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): The five relation types, their directions, and cycle rules. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): What Free and Pro each include. --- # Exporting Your Ideas Source: https://docs.neuralconfig.com/neuralrepo/integrations/export/ Your ideas are yours. NeuralRepo exports the whole repository in three formats — **JSON**, **CSV**, and a **Markdown vault** — and every one of them works on **every plan**, including Free. | Format | Best for | You get | |---|---|---| | **JSON** | Backups, scripting, migrating to another tool | One document with your profile, every idea, its tags, links, and relations, plus your tag list | | **CSV** | Spreadsheets, quick triage, sharing a list | One flat row per idea: `id`, `number`, `title`, `body`, `status`, `tags`, `source`, `created_at` | | **Markdown** | Obsidian and other vault-shaped tools | A `.zip` of one note per idea, with YAML frontmatter and relations as `[[wiki-links]]` | **Danger:** **Export before you archive, not after.** Every export excludes archived ideas. An export taken after a clear-out will not contain what you cleared out unless you [restore](https://docs.neuralconfig.com/neuralrepo/organize/delete-data/#how-do-i-restore-an-archived-idea) those ideas first, and permanently deleted ideas are gone for good. ## From the Web App 1. Open **Settings ▸ Data** in the NeuralRepo dashboard. 2. Choose **Export JSON**, **Export CSV**, or **Export Markdown**. 3. The file downloads immediately, named with today's date — `neuralrepo-export-2026-07-26.json`, `neuralrepo-ideas-2026-07-26.csv`, or `neuralrepo-markdown-2026-07-26.zip`. ## From the API All three are `POST` requests — they are actions, not reads — and all three accept any credential. **JSON** ```bash curl -X POST https://neuralrepo.com/api/v1/user/export \ -H "X-API-Key: nrp_your_key_here" \ -o export.json ``` Returned as a JSON **body**, not a file download, so pipe it to `jq` directly if you prefer. **CSV** ```bash curl -X POST https://neuralrepo.com/api/v1/user/export/csv \ -H "X-API-Key: nrp_your_key_here" \ -o export.csv ``` Sent as `text/csv` with a UTF-8 byte-order mark, so Excel opens accented characters correctly. **Markdown** ```bash curl -X POST https://neuralrepo.com/api/v1/user/export/markdown \ -H "X-API-Key: nrp_your_key_here" \ -o vault.zip ``` Sent as `application/zip`. Unzip it straight into a vault folder. ## What the Markdown Vault Looks Like One note per idea, named from the idea's **display number** zero-padded to four digits plus a slug of its title: ``` 0042-browser-extension-for-idea-capture.md 0043-content-script-for-text-selection.md 0055-habit-tracker-with-streaks.md ``` Each note carries frontmatter, the body as-is, and — where they exist — a links section and a relations section: ```markdown --- number: 42 title: "Browser extension for idea capture" status: building source: claude-mcp tags: ["chrome","extension"] created: 2026-03-24 10:30:00 updated: 2026-04-02 08:12:44 --- # Browser extension for idea capture Highlight text on any page and save it straight to NeuralRepo... ## Links - [Source repo](https://github.com/org/repo) ## Related - [[0043-content-script-for-text-selection]] (parent) - [[0038-chrome-plugin-for-code-snippets]] (related) ``` Because relations become wiki-links, **Obsidian's graph view reproduces your mind map** — the same edges, the same clusters. Relations are written from both ends, so opening either note shows the connection. **Note:** Tags are exported as a frontmatter array rather than inline `#tags`. Obsidian indexes both, but if you rely on inline tags, add `tags` to your vault's frontmatter property settings. ## What Exports Leave Out Worth knowing before you treat an export as a complete backup: - **Archived ideas.** Excluded from all three formats, with no way to include them. - **Anything past 10,000 ideas.** Every export is capped at 10,000 ideas. - **Relations pointing at archived ideas.** A wiki-link to a note that would not exist is dropped rather than left dangling. - **Duplicate detections and the review queue.** Detection state is working data, not content, and is not exported. - **Your API keys and BYOK keys.** The JSON export includes your profile and `settings_json`, but never a credential. - **Agent conversations.** Chat history lives in the agent's own storage and is not part of an export. ## Exporting a Single Idea For one idea rather than the whole repository, the CLI writes a Markdown file plus its context into a directory of your choosing: ```bash nrepo pull 134 --to ./my-project ``` This is the same shape as a vault note and is meant for pulling an idea into a project you are about to build. Note that the `.neuralrepo` file written alongside it is never read back — editing the pulled file changes nothing upstream. ## There Is No Import Exports are one-way. NeuralRepo has no endpoint that takes an export file and restores it: bringing ideas back in means replaying them through `POST /ideas` or `nrepo push`, which creates **new** ideas with new identifiers and re-runs embedding, duplicate detection, and auto-tagging. The [Migration guide](https://docs.neuralconfig.com/neuralrepo/guides/migration/) covers scripting that, along with the Free plan's 50-idea and 100-request-per-day ceilings. ## Next Steps - [User & Settings API](https://docs.neuralconfig.com/neuralrepo/api/user/): The three export endpoints alongside the rest of the user surface. - [Migration](https://docs.neuralconfig.com/neuralrepo/guides/migration/): Getting ideas in — from Notion, Obsidian, JSON, or plain Markdown. - [nrepo pull](https://docs.neuralconfig.com/neuralrepo/cli/pull/): Export a single idea and its context into a project directory. - [Data Model](https://docs.neuralconfig.com/neuralrepo/architecture/data-model/): What the JSON export's fields mean, table by table. --- # MCP Server for Claude Source: https://docs.neuralconfig.com/neuralrepo/integrations/mcp/ **Tip:** This page covers the v1 endpoint, `/mcp/`. The newer endpoint, `https://neuralrepo.com/mcp/v2`, has 22 discrete tools (no `code_mode`) and is the one listed in the Claude connector directory. See [Claude Connector (MCP v2)](https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/). NeuralRepo exposes a **remote MCP server** that gives Claude direct access to your idea repository. Save, search, update, link, and delete ideas without ever leaving your conversation. ## What Is MCP? The [Model Context Protocol](https://modelcontextprotocol.io) is an open standard for connecting AI assistants to external tools and data. NeuralRepo implements an MCP server at: ``` https://neuralrepo.com/mcp/ ``` When connected, Claude gains nine tools for managing your ideas: eight named tools plus `code_mode`, which reaches the rest of the API. Authorization is handled through OAuth 2.0 with PKCE, so your data stays secure. **Note:** Every tool that takes an idea identifier takes the **database `id`**, never the `#number` shown in the web app and the CLI. The tools return both fields, so ask Claude for the `id` when you want to cross-reference an idea by hand. See [Idea identifiers](https://docs.neuralconfig.com/neuralrepo/api/overview/#idea-identifiers). ## Setup **Claude.ai** 1. Open [Claude.ai](https://claude.ai) and go to **Settings ▸ Connectors**. 2. Click **Add custom connector**. 3. Paste the NeuralRepo MCP URL: ``` https://neuralrepo.com/mcp/ ``` 4. Save. On your next conversation, Claude will have access to NeuralRepo tools. 5. The first time Claude uses a tool, you will be prompted to **authorize via OAuth**. Sign in with your NeuralRepo account to grant access. The same connector works in Claude Desktop and the Claude mobile apps once it is added to your account. **Claude Desktop** Add the following to your Claude Desktop configuration file (`claude_desktop_config.json`): ```json { "mcpServers": { "neuralrepo": { "transport": "http", "url": "https://neuralrepo.com/mcp/" } } } ``` On macOS, this file is located at `~/Library/Application Support/Claude/claude_desktop_config.json`. On Windows, it is at `%APPDATA%\Claude\claude_desktop_config.json`. Restart Claude Desktop after saving. The OAuth prompt appears on first tool use. **Claude Code** Run the following command in your terminal: ```bash claude mcp add --transport http neuralrepo https://neuralrepo.com/mcp/ ``` On the next Claude Code session, NeuralRepo tools are available. OAuth authorization triggers automatically on first use. **Tip:** The NeuralRepo dashboard has the same instructions built in, at **Settings ▸ Integrations ▸ MCP Connector** — including a **Copy** button for the URL and a **Test connection** button that reports back `Connected` or the error it hit. ## OAuth Flow NeuralRepo uses **OAuth 2.0 with PKCE (S256)** for MCP authorization: 1. Claude attempts to call a NeuralRepo tool. 2. The MCP client registers itself if it has not already — NeuralRepo supports [Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591) at `/mcp/register`, and a registration is kept for 30 days. 3. The MCP client initiates an authorization request with a PKCE code challenge. It must send `code_challenge_method=S256`; a request with no challenge, no method, or `plain` is refused with `invalid_request`. 4. You are redirected to the NeuralRepo consent screen where you sign in (GitHub, Google, Apple, or magic link). If you haven't accepted the current Terms and Privacy Policy, you accept them first. 5. You grant Claude the requested scopes: `ideas:read` and `ideas:write`. 6. NeuralRepo issues an access token and a refresh token. 7. The MCP client stores the tokens and refreshes as needed. | Credential | Lifetime | |---|---| | Authorization code | 10 minutes, single use | | Access token | 30 days | | Refresh token | 90 days, replaced on every refresh | | Client registration | 30 days | Scopes are enforced per tool. `save_idea`, `update_idea`, `delete_idea`, `link_ideas`, `unlink_ideas`, and `code_mode` all require `ideas:write`; everything else needs `ideas:read`. **Caution:** Revoke MCP access from **Settings ▸ Connections ▸ Connected MCP Clients** in the NeuralRepo web dashboard. Two things to know: the **Connections tab is Pro-only**, so a Free account that has connected Claude has no in-app revoke button, and the group only appears once at least one client has completed the OAuth flow. ## Available Tools ### save_idea Create a new idea in your repository. Ideas saved this way carry `source: "claude-mcp"`. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `title` | string | Yes | Idea title (max 200 chars) | | `body` | string | No | Full description in markdown (max 50,000 chars) | | `tags` | string[] | No | Tags to apply (max 20, each max 50 chars) | | `source_url` | string | No | URL where the idea originated (max 2,000 chars) | | `status` | string | No | `captured`, `exploring`, `building`, `shipped`, `shelved` | | `parent_id` | number | No | Parent idea `id` for sub-ideas — must exist, or the call fails | ```json { "saved": true, "idea": { "id": 138, "number": 42, "title": "Browser extension for idea capture", "body": null, "status": "captured", "source": "claude-mcp", "source_url": null, "parent_id": null, "vectorize_id": null, "is_archived": 0, "created_at": "2026-03-24 10:30:00", "updated_at": "2026-03-24 10:30:00", "tags": ["chrome", "extension"] } } ``` Embedding, duplicate detection, and auto-tagging run [asynchronously](https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/) after the tool returns, so `vectorize_id` is null in the response and any auto-applied tags land seconds later. **Caution:** **The Free plan's 50-idea limit is reported as a success, not an error.** Once a Free account holds 50 unarchived ideas, `save_idea` returns the plain sentence "You've captured 50 ideas on the free plan. Upgrade to Pro for unlimited ideas." with no error flag — Claude will usually relay it, but nothing in the response shape says the save failed. ### update_idea Modify an existing idea, or change status and tags on up to 50 ideas at once. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `id` | number | Yes (single mode) | Idea `id` to update. Mutually exclusive with `ids`. | | `ids` | number[] | Yes (bulk mode) | Up to 50 idea `id`s. Mutually exclusive with `id`. | | `title` | string | No | New title — **single mode only** | | `body` | string | No | New body — **single mode only** | | `status` | string | No | New status | | `tags` | string[] | No | Replace all tags | | `add_tags` | string[] | No | Append tags without removing existing | | `remove_tags` | string[] | No | Remove specific tags | | `link_to` | object | No | Create a relation to another idea in the same call — **single mode only**. Sub-fields: `idea_id` (number, required), `relation_type` (string, required — one of `related`, `blocks`, `inspires`, `parent`, `supersedes`), `note` (string, optional). | `tags` cannot be combined with `add_tags` or `remove_tags`. Bulk mode rejects `title`, `body`, and `link_to`, and requires at least one of `status` or a tag operation. **Single mode** returns the updated idea, plus `relation_created` when `link_to` was used: ```json { "updated": true, "idea": { "id": 138, "number": 42, "status": "building", "tags": ["chrome"] }, "relation_created": { "id": 17, "relation_type": "related", "target_idea_id": 96 } } ``` **Bulk mode** is not atomic — it reports per-idea outcomes and keeps going after a failure: ```json { "updated": 2, "errors": 1, "results": [ { "id": 137, "status": "updated" }, { "id": 138, "status": "updated" }, { "id": 96, "status": "error", "error": "Idea 96 not found" } ] } ``` ### delete_idea Archive an idea. The row survives with `is_archived = 1`, and its vector is deleted from the search index. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `id` | number | Yes | Idea `id` to archive | ```json { "deleted": true, "id": 138, "title": "Browser extension for idea capture" } ``` **Danger:** **There is no way back.** Nothing in NeuralRepo — no tool, endpoint, CLI command, or dashboard control — clears `is_archived`, and archived ideas are excluded from every export. The tool's own description says it "soft-deletes so it can be recovered later"; that is wrong. Treat `delete_idea` as permanent and ask Claude to confirm before calling it. ### search_ideas Search your ideas by meaning, or browse them by recency. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `query` | string | No | Search query, max 500 chars. Omit to browse/list. | | `status` | string | No | Filter by status | | `tag` | string | No | Filter by tag name | | `limit` | number | No | Max results (max 100). Default 5 with a query; see below without one. | | `offset` | number | No | Results to skip (browse mode) | **With a query (search mode)** — semantic search over your embeddings, reranked, with everything below your score threshold dropped: ```json { "query": "browser extension", "ideas": [ { "id": 138, "number": 42, "title": "Browser extension for idea capture", "status": "captured", "tags": ["chrome"], "score": 0.92 } ], "search_type": "semantic" } ``` `search_type` tells you which path answered: | Value | Meaning | |---|---| | `semantic` | Vector search returned matches above the threshold | | `fts_fallback` | Semantic search returned nothing or errored, so keyword search answered — or a Free account has spent its 10 semantic searches for the month, in which case a `notice` field explains that | | `browse` | No query was given | **Note:** This automatic keyword fallback is **specific to the MCP tool and the AI agent**. The REST API and the `nrepo` CLI return an empty result set instead of falling back. See [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/). **Without a query (browse mode)** — and this is the surprising one: with no `limit` and no `offset`, the tool auto-paginates and returns **every** idea you have, in pages of 100: ```json { "ideas": [ { "id": 138, "number": 42, "title": "Browser extension for idea capture", "status": "captured", "tags": ["chrome"] } ], "count": 1, "search_type": "browse" } ``` Pass an explicit `limit` (or a non-zero `offset`) to get one page instead. That response adds `offset`, `limit`, and `has_more`, and its `count` is the **total** matching your filters, not the number of rows returned. Filtering by a tag that does not exist is not an error — you get an empty list and a hint: ```json { "ideas": [], "count": 0, "hint": "Tag \"chorme\" does not exist. Use list_tags to see available tags." } ``` ### get_idea Retrieve a single idea with full details. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `id` | number | Yes | Idea `id` | | `include_context` | boolean | No | Include related ideas, links, and duplicate detections (default `false`) | Without `include_context` you get the idea row plus `tags`, `links`, and `relations`. With it, the tool returns **two content blocks**: a Markdown project-context brief Claude can read directly, and the same data as JSON. Related ideas are deduplicated, so an idea reachable by two relation types appears once with a `relation_types` array. ### list_tags Returns all tags in your repository with idea counts. No parameters. ```json { "tags": [ { "name": "chrome", "color": "#4285f4", "idea_count": 3 }, { "name": "mobile", "color": null, "idea_count": 7 } ], "count": 2 } ``` ### link_ideas **Note:** Create typed relations between ideas, one at a time or up to 50 in a batch. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `source_idea_id` | number | Yes (single mode) | Source idea `id` | | `target_idea_id` | number | Yes (single mode) | Target idea `id` | | `relation_type` | string | No | `related`, `parent`, `blocks`, `inspires`, `supersedes` (default `related`) | | `note` | string | No | Explanation of the relationship (max 500 chars) | | `links` | object[] | Yes (bulk mode) | Up to 50 link objects with the same fields. Mutually exclusive with `source_idea_id`/`target_idea_id`. | `parent` reads **source is a child of target**, so linking 138 → 96 as `parent` makes 96 the parent. Cycles are refused outright for `blocks` and `parent`; `supersedes` cycles are allowed. Bulk mode returns `{ linked, errors, results }` with a per-link outcome, so one bad pair does not kill the batch. ### unlink_ideas **Note:** Remove a relation between ideas. Use `get_idea` with `include_context: true` to find relation IDs first. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `relation_id` | number | Yes | Relation ID to remove | The response is `{ "unlinked": true, "relation_id": 17 }` whether or not a relation with that ID existed — a no-op and a real deletion look identical. ### code_mode Execute multi-step operations against the NeuralRepo API in a single call. Useful for workflows that combine search, filter, update, and link operations, or for accessing capabilities not exposed as discrete tools (merge ideas, manage duplicates, CRUD tags, attach URL links, get stats, develop specs, update relations). | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `code` | string | Yes | Async JavaScript function body, **max 10,000 characters**. Calls snake_case functions on the `neuralrepo` object (e.g., `neuralrepo.search_ideas()`, `neuralrepo.update_idea()`). | The code runs in a sandboxed Worker with your user and plan applied, so plan gates and free-tier limits behave exactly as they do through the named tools. ## Tips **Tip:** **Search before you save.** Before asking Claude to save a new idea, ask it to search first. This avoids duplicates and often surfaces existing ideas you forgot about. - Ask Claude to tag ideas as it saves them — "save this idea and tag it with `mobile` and `mvp`." - Use `update_idea` in bulk mode to retag or move a batch: "move all my browser extension ideas to exploring." - Use `include_context: true` on `get_idea` to see the full network around an idea. - Ask for the `id` when you plan to use an idea elsewhere — the `#42` Claude quotes back may be the display number. ## Troubleshooting | Issue | Solution | |-------|----------| | **`invalid_request` — `code_challenge_method must be S256`** | The client started authorization without PKCE, or without stating `code_challenge_method=S256`. NeuralRepo accepts only S256 PKCE for new connections. Claude's custom connectors send it; for another client, check that it supports PKCE with S256. | | **OAuth prompt keeps appearing** | Revoke the client in **Settings ▸ Connections ▸ Connected MCP Clients** (Pro) and re-authorize from scratch. | | **Connection timeout** | Verify your network allows outbound HTTPS to `neuralrepo.com`. Corporate firewalls may block MCP transport. | | **"Not authorized" error** | Your refresh token may have expired (90-day limit) or been revoked. Re-authorize via the OAuth flow. | | **"Insufficient scope"** | The token was issued without `ideas:write`. Re-authorize and grant both scopes. | | **Tool not appearing in Claude** | Ensure the MCP server is correctly configured. In Claude Code, run `claude mcp list` to verify. | | **Linking fails with "requires a Pro plan"** | `link_ideas` and `unlink_ideas` are Pro-only. Every other tool works on Free. | | **"Idea N not found" for an idea you can see** | You passed the display `#number` instead of the database `id`. | | **Rate limited** | MCP requests don't count toward the daily REST API limit (100/day Free, 10,000/day Pro), and the MCP server has no request limit of its own. On Free, `search_ideas` allows 10 semantic searches a month, then falls back to keyword search with a note; that is a fallback, not an error. | ## Next Steps - [Capture from Claude](https://docs.neuralconfig.com/neuralrepo/capture/mcp/): Day-to-day capture patterns once the connector is live. - [Claude Code](https://docs.neuralconfig.com/neuralrepo/integrations/claude-code/): Pair the connector with the nrepo CLI for terminal workflows. - [API Overview](https://docs.neuralconfig.com/neuralrepo/api/overview/): The REST surface code mode reaches, including idea identifiers and plan gates. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): What Free and Pro each include, including relation writes. --- # Siri Shortcuts Setup Source: https://docs.neuralconfig.com/neuralrepo/integrations/siri-shortcuts/ NeuralRepo ships **one pre-built Siri Shortcut — Quick Capture**. Add it once and say **"Hey Siri, NeuralRepo"** to save an idea from anywhere you are signed in to the same Apple account, including your Apple Watch. **Note:** **Apple Shortcuts is a Pro feature.** The card in **Settings ▸ Integrations** shows an upgrade link on the Free plan, and the API key the shortcut needs can only be created on Pro. ## What You Get **Quick Capture** Prompts you to dictate or type an idea title and sends it to NeuralRepo with status `captured` and source `siri`. The fastest path from thought to saved idea, and the only shortcut NeuralRepo publishes. **Everywhere your Apple ID is** It is an iCloud shortcut, so adding it on one device syncs it to iPhone, iPad, Mac, Apple Watch, and HomePod. Put it on the Home Screen, in a widget, or on the Action Button. **Caution:** Older versions of this page described **Share Sheet Capture** and **Clipboard Capture** shortcuts. They do not exist — `GET /user/shortcuts` returns a single `quick_capture_url`. For sharing a URL or selection from another app, share it to **Mail** and send it to your personal [email capture](https://docs.neuralconfig.com/neuralrepo/capture/email/) address. (The native iOS app has a share extension, but it is [not yet released](https://docs.neuralconfig.com/neuralrepo/capture/ios/).) ## Installation The shortcut is added from inside the NeuralRepo dashboard, not downloaded from this site. 1. **Open Settings ▸ Integrations** in the NeuralRepo web app and expand the **Apple Shortcuts** card. On a Free plan the card is titled **Apple Shortcuts — Pro** and offers an upgrade link instead of the setup UI. 2. **Generate an API key.** If you have no key yet, the card says so and offers **Generate API Key** directly. Keys are also managed in **Settings ▸ Connections**. Copy the key — it starts with `nrp_` and is 68 characters long. It is shown once and cannot be retrieved later. 3. **Click "Add to Shortcuts".** This opens NeuralRepo's Quick Capture shortcut in Apple's Shortcuts app via an iCloud link. If a key was just generated it is copied to your clipboard first, and the page tells you to paste it when Shortcuts asks. 4. **Paste your API key** when the shortcut prompts on first run. It is stored locally in the shortcut on your device. 5. **Test it.** Say **"Hey Siri, NeuralRepo"** or run Quick Capture manually from the Shortcuts app. **Caution:** Keep your API key private. Anyone with your key has full access to your NeuralRepo account. If you suspect it has been compromised, revoke it in **Settings ▸ Connections** and generate a new one — revocation takes effect immediately. ## How It Works The shortcut sends a `POST` request to the NeuralRepo API: ``` POST https://neuralrepo.com/api/v1/ideas X-API-Key: nrp_your_key_here Content-Type: application/json { "title": "Your dictated idea", "status": "captured", "source": "siri" } ``` The idea is created immediately; embedding, duplicate detection, and auto-tagging follow a few seconds later in the background. ## Apple Watch Siri Shortcuts run natively on Apple Watch (watchOS 9+). Because Quick Capture is an iCloud shortcut, it appears on the watch once it is on your Apple account — there is no separate watch app. - Raise your wrist and say **"Hey Siri, NeuralRepo"** - Dictate your idea - Siri confirms the capture **Tip:** Add Quick Capture to your watch face as a complication for one-tap access. See [Apple Watch](https://docs.neuralconfig.com/neuralrepo/integrations/apple-watch/) for the full walkthrough. ## Customization You can duplicate and modify the shortcut to fit your workflow — it is a normal Shortcuts document once added: - **Default tags:** add a `"tags": ["siri", "quick"]` field to the request body. - **Default status:** change `captured` to another status if you prefer. - **Multiple variants:** duplicate it into "NeuralRepo Work" and "NeuralRepo Personal" with different default tags. Each duplicate carries its own copy of the API key, so revoking a key breaks every variant built from it. ## Troubleshooting | Issue | Solution | |-------|----------| | **The card shows "Apple Shortcuts — Pro"** | Shortcuts capture requires Pro. The upgrade link on the card leads to the plan picker. | | **"Add to Shortcuts" is greyed out** | You have no API key yet. Generate one from the card, or in **Settings ▸ Connections**. | | **"Couldn't connect to NeuralRepo"** | Check your internet connection. The shortcut requires network access to reach the API. | | **"Unauthorized" error** | Your API key may be invalid or revoked. Re-enter a valid key in the shortcut settings. | | **Shortcut not appearing on Watch** | Ensure the shortcut is synced: open the Watch app on iPhone ▸ Siri ▸ check that it is listed. | | **Siri does not recognize the command** | Open the Shortcuts app, tap the shortcut, tap the share icon, and select "Add to Siri" to set a custom phrase. | | **Dictation is inaccurate** | Speak clearly and pause briefly. You can also type the idea by tapping the keyboard icon when Siri is listening. | ## Next Steps - [Capture with Siri](https://docs.neuralconfig.com/neuralrepo/capture/siri/): What the capture creates, and how Siri capture fits the other input methods. - [Apple Watch](https://docs.neuralconfig.com/neuralrepo/integrations/apple-watch/): Wrist capture in detail, including limitations and tips. - [Email Capture](https://docs.neuralconfig.com/neuralrepo/capture/email/): Share a link or a selection from any app by mailing it to your personal address. - [nrepo key](https://docs.neuralconfig.com/neuralrepo/cli/key/): Create, list, and revoke API keys from the terminal. --- # Weekly Digest & Stale Nudges Source: https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/ Every Sunday at **6:00 PM UTC**, NeuralRepo sends Pro users a weekly digest email summarizing idea activity. A separate Monday check nudges you (on any plan) about ideas that have been sitting untouched in `captured`. ## What the Digest Contains ### Without a BYOK Key A stats summary: - **New ideas this week** — how many ideas you captured in the last 7 days. - **Pending duplicates** — potential duplicates waiting for review. - **Stale ideas** — ideas stuck in `captured` past your stale threshold, with a nudge to move them forward or shelve them. - **Total ideas** — your active idea count. ### With a BYOK Key The same data, written up as a short narrative summary by your configured AI provider — including a specific suggestion based on the week's activity (e.g., which stale ideas to revisit). If the AI call fails, the digest falls back to the stats summary. **Tip:** The AI-written digest requires a configured BYOK key. See the [BYOK setup guide](https://docs.neuralconfig.com/neuralrepo/integrations/byok/) for instructions. **Note:** The digest is only sent when there is something to report — if a week has no new ideas, no pending duplicates, and no stale ideas, no email goes out. ## Stale Idea Nudges Separately from the digest, NeuralRepo checks every Monday at **9:00 AM UTC** for ideas that have been stuck in `captured` — created but never explored, built, or shelved. If you have any, you get a short email listing them with how long each has been untouched. - Available on **all plans** (the digest is Pro-only). - Sent **at most once a month** per user, even though the check runs weekly — a KV cooldown of 25 days means an ignored nudge won't repeat every Monday. - An idea counts as stale when it is in `captured` status, not archived, and hasn't been updated for more than your stale threshold (default: 30 days). Editing an idea resets the clock. - The email lists **at most 10 ideas**, each with how many days it has sat untouched, then "…and N more" if you have more than that. The subject line carries the full count. ## Delivery Schedule | Event | Schedule | Cron Expression | |-------|----------|-----------------| | Weekly digest (Pro) | Sunday 6:00 PM UTC | `0 18 * * SUN` | | Stale idea nudge check | Monday 9:00 AM UTC | `0 9 * * 1` | Both emails go to the address associated with your NeuralRepo account. ## Configuration Manage both emails from **Settings ▸ Notifications** in the web dashboard: - **Weekly digest email** — on by default for Pro accounts; toggle off to opt out. Only an explicit opt-out suppresses it; an account that has never touched the setting is opted in. - **Stale idea nudges** — on by default for all accounts; toggle off to opt out, on the same basis. - **Stale threshold** — how many days before a `captured` idea counts as stale (default: 30, accepted range 7–180). Applies to both the nudge emails and the digest's stale section. - **Preferred AI provider** — which BYOK provider writes the digest summary. That one lives in **Settings ▸ AI**, which is Pro-only. ## Testing the Digest You can trigger a test digest at any time without waiting for Sunday (it sends even if you've toggled the weekly digest off): ```bash curl -X POST https://neuralrepo.com/api/v1/user/test-digest \ -H "X-API-Key: nrp_your_key_here" ``` **Response `200 OK`:** ```json { "success": true, "content": "..." } ``` The `content` field contains the rendered digest content that was sent. **Note:** `POST /user/test-digest` has **no plan check**, so a Free account can send itself a digest even though the Sunday cron only serves Pro users. It also ignores the "nothing to report" skip and your notification opt-out — it always sends. Whether you get the AI narrative or the stats summary still depends on having a [BYOK key](https://docs.neuralconfig.com/neuralrepo/integrations/byok/), which does require Pro. ## How It Works Both emails are generated by **Cloudflare Workers Cron Triggers**: 1. The Sunday cron queries Pro users who haven't opted out of the digest. 2. For each user, it gathers the week's data: new ideas, pending duplicates, stale ideas, and totals. 3. If the user has a BYOK key, it asks the AI provider to write the summary; otherwise it formats the stats directly. 4. The digest is sent as an HTML email. The Monday cron checks every user (any plan) who hasn't opted out of nudges, skips anyone nudged within the last month, and emails a list of their stale ideas if there are any. ## Example Digest A typical AI-written digest looks like this: > You captured 5 new ideas this week, with a cluster forming around CLI improvements — that's your most active area this month. > > There are 2 potential duplicates waiting for review, and 3 ideas have been sitting in "captured" for over 30 days, including "GraphQL schema generator" and "Redis cache layer." Consider moving them to exploring or shelving them to clear the deck. > > Your repo now holds 47 active ideas. Nice momentum — keep it up. ## Next Steps - [BYOK AI Providers](https://docs.neuralconfig.com/neuralrepo/integrations/byok/): Configure your AI key to unlock the AI-written digest. - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): Understand the idea lifecycle and how statuses affect stale detection. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): The in-app version of the same nudge — duplicates, connections, and quiet ideas. - [Queue Processing](https://docs.neuralconfig.com/neuralrepo/architecture/queue-processing/): How the digest and nudge crons fit NeuralRepo's background jobs. --- # Delete Data Source: https://docs.neuralconfig.com/neuralrepo/organize/delete-data/ NeuralRepo deletes in **two steps**. **Archiving** an idea hides it but keeps it in the database, and you can restore it. **Permanently deleting** removes archived ideas for good, with everything attached to them. Deleting your **account** removes everything you own at once. Both permanent deletions happen in the web app (the **Archive** view and **Settings ▸ Delete**), and neither can be undone. ## What is the difference between archiving and deleting? | | Archive | Permanently delete | Delete account | |---|---|---|---| | **Where** | Web app, `nrepo rm`, `DELETE /ideas/:id`, MCP `delete_idea` / `archive_idea` | **Archive** view or **Settings ▸ Delete** (web only) | **Settings ▸ Delete** (web only) | | **Applies to** | One idea | Archived ideas only | Everything you own | | **Idea row** | Kept, marked `is_archived = 1` | Deleted | Deleted | | **Search vector** | Deleted at once | Deleted | Deleted | | **Tags, links, relations, duplicate records** | Kept with the archived idea | Deleted | Deleted | | **Can be undone** | Yes: restore it from the **Archive** view, `POST /ideas/:id/restore` or MCP `restore_idea` | No | No | An archived idea leaves every list, search, export and the mind map until you restore it (see [Delete Idea](https://docs.neuralconfig.com/neuralrepo/api/ideas/#delete-idea)). What permanent deletion adds is removing the data itself. ## How do I restore an archived idea? Open **Archive** in the sidebar (on a phone: **Settings ▸ Delete ▸ Browse the archive**) and click **Restore** next to the idea. It goes back to your lists with its tags, links and relations, and is re-indexed for semantic search within a minute or so. Over the API use [`POST /ideas/:id/restore`](https://docs.neuralconfig.com/neuralrepo/api/ideas/#restore-idea); from Claude, the [connector](https://docs.neuralconfig.com/neuralrepo/integrations/claude-connector/)'s `restore_idea` tool. On the Free plan a restored idea counts toward the 50-idea cap again, so restoring is refused while you already have 50 active ideas. Archive another one first, or upgrade. ## How do I permanently delete archived ideas? To delete **one** archived idea, open **Archive** in the sidebar, click **Delete** next to it and confirm the prompt. To delete them **all** at once, use **Delete all** there, or: 1. Open **Settings ▸ Delete** in the web app. The **Delete Data** section shows how many archived ideas you have. 2. Click **Permanently delete archived ideas** and confirm the prompt (*Permanently delete N archived ideas? This cannot be undone.*). 3. The page reports *Deleted N archived ideas.* This deletes **every** archived idea at once. It removes each idea with its tags, links, relations, duplicate records and search vectors. A tag that no remaining idea uses is deleted too. A tag still in use has its search vector rebuilt from the ideas that remain, so a deleted idea's title no longer shapes it. The button is greyed out when you have no archived ideas. Live ideas are never touched, so to permanently delete an idea, archive it first. **Only a signed-in browser can do this:** Permanent deletion needs a web session. An API key or an MCP token gets `403` with `Sign in on the web to permanently delete ideas`, whatever its scopes, so neither a script nor Claude can destroy data irreversibly. The endpoints are listed under [Ideas](https://docs.neuralconfig.com/neuralrepo/api/ideas/#permanently-delete-archived-ideas). ## How do I delete my account? 1. **Export first if you want a copy.** Deletion can't be undone, and archived ideas are never in an export. See [Export](https://docs.neuralconfig.com/neuralrepo/integrations/export/). 2. Open **Settings ▸ Delete** in the web app and go to **Delete account**. 3. Type your account email into the box. The **Delete account** button stays disabled until it matches (letter case doesn't matter). 4. Click **Delete account**. You are signed out and returned to the home page. You can do this even if NeuralRepo is waiting for you to accept updated Terms. Like permanent deletion, it needs a web session: an API key or MCP token gets `403` `Sign in on the web to delete your account`. ## What does deleting my account remove, and what is kept? **Removed:** - Every idea, archived or not, with its tags, links, relations, duplicate records and attachments - Every search vector for your ideas and tags - Your AI agent chat history - Your API keys, connected MCP clients (their access and refresh tokens), sessions, sign-in links, and the GitHub, Google or Apple sign-ins attached to the account - Your saved AI provider keys (BYOK), settings and email capture address - Any subscription: **it is canceled immediately**, along with any checkout still open **Kept:** | Record | Why | |---|---| | Which Terms, Privacy Policy and addenda versions you accepted, and when | The legal record of consent. Your email in the account row is replaced with a one-way hash, and every other personal, payment and BYOK field is cleared. | | Stripe payment history (amounts, Stripe IDs) | The financial record | | AI usage counts (model, token counts, cost) | Detached from you, then deleted by the 90-day sweep below | **Out of reach**: deleting your account can't touch these. The `DELETE /user/me` response lists them in `out_of_reach`. The web app goes straight to the home page and doesn't show that list, and its warning before you delete names only some of them (it leaves out D1 Time Travel): - Cloudflare Workers Logs and AI Gateway logs, which age out on their own retention - Resend's email delivery logs - Stripe's customer, invoice and payment records - What Anthropic, OpenAI or OpenRouter received under your own API key, per your agreement with them - Database point-in-time restore (D1 Time Travel), which keeps earlier states for up to 30 days **If deletion stops partway:** NeuralRepo cancels billing first, then deletes your API keys and MCP tokens, then search vectors and chat history, and only then removes the account. So a deletion that stops partway has already disconnected the CLI, Claude and any other MCP client; you stay signed in on the web. If any step fails, you get `Account deletion did not finish. You are still signed in; try again to complete it.` Run it again, and it picks up where it stopped. Signing in again later with the same email or provider creates a **new, empty** account. ## What is cleaned up automatically? Every day at **04:00 UTC**, a retention sweep deletes: | Data | Deleted when | |---|---| | Web sessions | Expired: 30 days after last use | | MCP authorization codes | Expired (10 minutes) or already exchanged | | Magic sign-in links | Expired: 15 minutes after they were sent, used or not | | MCP access keys | Older than 30 days. Sign-in already refuses them at 30 days, and the client refreshes or re-authorizes | | MCP refresh tokens | Expired (90 days) or replaced by a refresh | | AI usage records | Older than 90 days | The sweep never deletes ideas. Archived ideas stay until you permanently delete them. ## Next steps - [Export](https://docs.neuralconfig.com/neuralrepo/integrations/export/): Download every idea as JSON, CSV or Markdown before you delete anything. - [nrepo rm](https://docs.neuralconfig.com/neuralrepo/cli/rm/): Archive an idea from the terminal: the first of the two steps. - [Ideas API](https://docs.neuralconfig.com/neuralrepo/api/ideas/): The archive and permanent-delete endpoints, with their responses. - [Security](https://docs.neuralconfig.com/neuralrepo/architecture/security/): Sessions, API keys and MCP tokens, and how long each one lasts. --- # Duplicate Detection Source: https://docs.neuralconfig.com/neuralrepo/organize/duplicates/ When you save a new idea, NeuralRepo checks your existing ideas for potential duplicates using vector similarity. This keeps your repository clean without requiring you to remember every idea you have ever captured. ## How It Works Duplicate detection runs automatically as part of the idea creation pipeline: 1. A new idea is created and queued for processing. 2. The queue worker generates an embedding using the **@cf/baai/bge-m3** model. 3. The embedding is compared against your **five nearest** ideas in **Cloudflare Vectorize**. Shelved ideas are excluded from the comparison. 4. Any neighbour scoring **above the dedup threshold** (default **0.75**) gets a `duplicate_detections` record with status `pending`. 5. Any neighbour scoring **above the related threshold** (default **0.55**) gets an automatic `related` relation, unless the pair is already linked or you removed an automatic link between them before. ```mermaid flowchart TD save["Idea saved<br/>(any capture channel)"] --> queue["Queued for processing"] queue --> embed["Embed title + body<br/>@cf/baai/bge-m3"] embed --> query["Query Vectorize:<br/>5 nearest, shelved excluded"] query --> dedup{"score > 0.75?"} query --> rel{"score > 0.55?"} dedup -->|yes| pending["Pending duplicate detection"] rel -->|yes| related["Auto 'related' relation"] pending --> resolve["Merge or dismiss"] ``` Both thresholds are evaluated independently, so a pair above 0.75 clears 0.55 too: a detected duplicate is **also** linked with a `related` relation. No `duplicate` relation is ever created for you — that type is reserved for links you make yourself. **Note:** Detection runs asynchronously, after the save. There is a brief delay — typically a few seconds — between capturing an idea and its pair appearing for review. Nothing warns you during the save itself. **Caution:** Editing an idea's title or body **re-runs the whole pipeline**: its pending detections and its system-created relations are deleted first, then recomputed against the edited text. Relations you created by hand survive. A duplicate you were about to merge can therefore disappear — or a new one appear — after an edit. ## Who Sees Detections Detections are computed for **every** account. The Pro plan is what displays them: on the free plan the duplicates list returns empty, and the Duplicates view is replaced by an upgrade panel. Nothing is lost in the meantime — the backlog is recorded and appears in full the moment you upgrade. ## Viewing Duplicates **Web** **Duplicates** in the sidebar carries an amber count when pending detections exist. It opens a list of pairs with their similarity score, each offering **Merge** or **Not a duplicate**. The same pairs appear at the top of the [Review queue](https://docs.neuralconfig.com/neuralrepo/organize/review/). **CLI** ```bash # List all pending duplicate detections nrepo duplicate list # Output: # Detection #1 Score: 0.92 # Idea 42: "Background job processing system" # Idea 58: "Async worker queue for tasks" # # Detection #2 Score: 0.88 # Idea 11: "Mobile habit tracker" # Idea 37: "Daily routine tracking app" ``` **API** ```bash GET /api/v1/ideas/duplicates ``` Response: ```json { "duplicates": [ { "id": 1, "idea_id": 58, "idea_title": "Async worker queue for tasks", "duplicate_of_id": 42, "duplicate_title": "Background job processing system", "similarity_score": 0.92, "status": "pending", "created_at": "2026-03-20T14:30:00Z" } ] } ``` ## Resolving Duplicates Every pending detection must be resolved with one of two actions: **dismiss** or **merge**. ### Dismiss If the detection is a false positive — the ideas are similar but distinct — dismiss it. The detection is marked as `dismissed` and will not appear again. **API** ```bash POST /api/v1/ideas/duplicates/1/dismiss ``` **Web** Click the **Dismiss** button on the detection card in the duplicates panel. ### Merge If the ideas are truly duplicates, merge them. Merging folds one idea into the other and archives the loser. Both merge paths share the same core: 1. **Bodies are concatenated** — the absorbed body is appended to the kept one, separated by a `---` rule. 2. **Tags are unioned** — every tag from both ideas ends up on the kept idea. (The 20-tag cap is applied when you *submit* tags, not on merge, so a merge can leave an idea holding more than 20.) 3. **The kept idea keeps the earlier `created_at`** of the two, so it sorts where the original did. 4. **The absorbed idea is archived** — out of every list, search, and relation query, but retained in the database. **Caution:** **No `supersedes` relation is created.** Archived ideas are filtered out of relation queries, so the link would be invisible; the merge deliberately skips it. If you want a visible pointer between two ideas, dismiss the detection and create a relation instead of merging. Where the two paths differ: | | Resolving a detection (web, `nrepo duplicate merge`, API) | Merging two ideas directly (`nrepo merge`, API) | |---|---|---| | Which idea survives | The **newer** one — the idea that triggered the detection | The one you name first (`keep-id`) | | Which is archived | The **older** existing idea it matched | The one you name second (`absorb-id`) | | Relations on the absorbed idea | Left behind with the archived idea | **Transferred** to the kept idea | | Status of the absorbed idea | Archived, status unchanged | Archived **and** moved to `shelved` | **CLI** ```bash # Resolve detection #1 from the pending list nrepo duplicate merge 1 # Or merge two ideas directly: keep 42, absorb 58 nrepo merge 42 58 ``` **API** ```bash # Resolve a detection — keeps detection.idea_id, archives duplicate_of_id POST /api/v1/ideas/duplicates/1/merge # Merge directly — keeps 42, absorbs 58, moves its relations across POST /api/v1/ideas/42/merge { "absorb_id": 58 } ``` **Web** Click **Merge** on the pair, in either the Duplicates view or the Review queue. The detection is marked `merged` and leaves the queue. **Caution:** Merging is not reversible from the UI or the API. Review the two ideas carefully first. If you are unsure, dismiss the detection — you can always create a `related` relation to keep the connection visible. ## Configuring the Threshold The dedup threshold controls how similar two ideas must be to trigger a duplicate detection. Adjust it in your user settings. | Setting | Range | Default | Effect | |---------|-------|---------|--------| | `dedup_threshold` | 0.1 - 0.9 | **0.75** | Higher = fewer detections, only near-exact matches | | `related_threshold` | 0.1 - 0.9 | **0.55** | Neighbours above this get an automatic `related` relation | Both comparisons are strictly greater-than, and both apply only to the five nearest neighbours — raising `related_threshold` above `dedup_threshold` stops the automatic relations without stopping detection. ```bash curl -X PATCH https://neuralrepo.com/api/v1/user/me \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"settings_json": "{\"dedup_threshold\": 0.80}"}' ``` **Tip:** If you are seeing too many false positives, raise the dedup threshold to 0.90. If real duplicates are slipping through, lower it to 0.80. ## Next Steps - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): The other place duplicates surface — alongside quiet ideas and new connections. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): Relation types, cycle rules, and which relations NeuralRepo creates for you. - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): The same embeddings power semantic search — and the same threshold settings. - [nrepo duplicate](https://docs.neuralconfig.com/neuralrepo/cli/duplicate/): List, dismiss, and merge detections from the terminal. --- # Mind Map Source: https://docs.neuralconfig.com/neuralrepo/organize/mind-map/ The mind map is a force-directed graph that renders your ideas as nodes and their relations as edges. It gives you a bird's-eye view of how your thinking connects and evolves. **Caution:** Free-tier users can open the mind map in **view-only mode** — explore the graph, click nodes, filter, zoom, and pan. Editing (dragging nodes, creating or removing links, changing statuses) requires **Pro**. The agent chat panel and semantic layout are not generally available (see below). ## How It Looks The map follows your theme (dark or light). Nodes are flat rounded cards with a status-coloured border and dot, and edges are styled by relation type. Highly-connected ideas appear larger, making hubs easy to spot. ## Visual Language The map uses a consistent visual language so you can read relationships at a glance. ### Node Anatomy Each node is a card showing: - **Title** — the idea name (truncated if long) - **Status dot** — colored circle in the top-right corner - **Tags** — up to 2 tag pills shown below the title - **Connection count** — badge in the bottom-right showing how many relations exist Node **width scales with connection count** — ideas with more relations appear larger, making hub ideas easy to spot. ### Edge Types Edges encode the relation type through color, weight, and style: | Relation | Color | Style | Arrow | Meaning | |----------|-------|-------|-------|---------| | `related` | Purple | Thin solid | No | General connection | | `parent` | Light | Solid, bold | Yes | Hierarchical parent → child | | `blocks` | Red | Solid, bold | Yes | A blocks progress on B | | `inspires` | Cyan | Solid, medium | Yes | A inspired the creation of B | | `duplicate` | Amber | Dashed | No | Auto-detected duplicate | | `supersedes` | Gray | Dashed | Yes | A replaces B | Edges created by NeuralRepo from similarity carry a `score`, which the map shows as a percentage; edges you created have `score: null`. The map also distinguishes them visually — **system edges are drawn fainter and thinner, your own edges brighter and roughly twice as wide** — so a graph you have curated stands out from one that assembled itself. ## Interaction | Action | How | |--------|-----| | **Zoom** | Scroll wheel or pinch gesture | | **Pan** | Click and drag on the background | | **Open idea** | Click a node to open the idea detail panel | | **Highlight connections** | Hover a node to highlight its direct connections | | **Drag-to-connect** | Drag from a node handle to another node to create a relation | | **Context menu** | Right-click a node for quick actions (move status, link, focus, archive) | | **Focus mode** | Right-click → Focus to dim everything except the 2-hop neighborhood | | **Filter by status** | Status toggles above the graph show/hide ideas by status | | **Filter by tag** | Tag filter narrows the graph to ideas with specific tags | | **Timeline** | Adjustable time window with a brushable density chart showing idea creation frequency. Drag the beginning and end handles to set the visible range, use preset chips (1d, 7d, 30d, All) for quick navigation, or press play to sweep a playhead through the window. Keyboard shortcuts: Space for play/pause, Home to reset, Arrow keys to pan. | ### Focus Mode Right-click any node and select **Focus** to highlight its 2-hop neighborhood. All other nodes dim to 20% opacity, letting you trace the immediate context of an idea without visual clutter. ### Drag-to-Connect Hover a node to reveal four connection handles. Drag from a handle to another node to create a new relation. A magnetic snap activates within 15px of the target, and a relation type picker appears on drop. ## API Access Retrieve the full graph data to build custom visualizations. ```bash curl https://neuralrepo.com/api/v1/map \ -H "X-API-Key: nrp_your_key_here" ``` Response: ```json { "nodes": [ { "id": 42, "title": "Background job processing system", "status": "building", "tags": ["backend", "infrastructure"], "connectionCount": 5, "created_at": "2026-03-15T10:00:00Z" } ], "edges": [ { "id": 1, "source": 10, "target": 42, "type": "blocks", "score": null, "note": "Need auth system before dashboard", "created_at": "2026-03-16T09:30:00Z" } ] } ``` ## CLI Exploration Use `nrepo graph` to explore the graph from any idea without opening the web app. ```bash # Immediate connections nrepo graph 42 # 3 levels deep, filtered to blocks and inspires nrepo graph 42 --depth 3 --type blocks,inspires ``` ``` 42: Background job processing system [building] ├── blocks → 55: Admin dashboard [exploring] ├── blocks → 60: API rate limiting [captured] └── inspires → 71: Event-driven architecture [exploring] └── inspires → 82: Real-time notifications [captured] ``` **Tip:** Use `nrepo graph` with `--depth 1` for a quick overview, or increase the depth to trace chains of influence and dependency across your idea network. ## 3D Mind Map Toggle the **3D** button in the toolbar to switch from the flat force-directed layout to an immersive three-dimensional view. The 3D view is powered by **Three.js** and **3d-force-graph**. It uses the same graph data as the 2D view with a few visual differences: - **Node labels** — rendered as CSS2D overlays (always face the camera) - **Weak edges** (`related`, `duplicate`) — rendered as directional particle streams instead of dashes (3d-force-graph does not support dashed links) - **Strong edges** (`parent`, `blocks`, `inspires`, `supersedes`) — rendered as solid lines with directional arrows - **Camera** — orbit with drag, zoom with scroll, pan with right-drag; auto-fits on load Clicking a node in 3D works the same as in 2D — opens the idea detail panel. Right-click opens the context menu. ### Performance Note The 3D view runs on WebGL. Very large graphs (500+ nodes) may be slower to navigate than the 2D view on lower-end hardware. ## Force Controls Click **Tune** in the toolbar to open the Force Tuning panel and adjust the physics in real time: | Slider | Range | Effect | |--------|-------|--------| | **Charge** | −2000 to −100 | How strongly ideas repel each other | | **Distance** | 40 to 400 | The ideal length of an edge | | **Strength** | 0.10 to 0.50 | How hard a relation pulls its two ideas together | | **Gravity** | 0 to 0.1 | How strongly everything is drawn toward the centre | | **Padding** | 0 to 40 | Clearance kept between node cards | | **Filter Reheat** | 0.10 to 1.00 | How much the layout re-settles after a filter change | | **Search Reheat** | 0.00 to 1.00 | How much it re-settles after a search | Changes take effect as you drag, with a burst of simulation energy (alpha 0.3) so ideas settle into the new arrangement. The panel also resets to the default preset in one click. You can save custom force configurations as **presets** and switch between them from the toolbar. Presets live in `localStorage`, so they are per-browser rather than per-account. ## Agent Chat Panel **Not generally available:** The agent chat panel, semantic layout, Synthesize and lasso actions are switched off for most accounts while the agent is reworked, so they don't appear on your map. This section describes them for accounts that still have them. The floating **AI agent chat panel** hovers over the mind map. Click the NeuralRepo icon in the bottom-right corner of the map to open it. The panel is **draggable** — grab the header to reposition it anywhere on the map. Its position is saved to `localStorage` per session. ### Model Selector A model selector appears at the top of the panel, offering five models: - **Anthropic** — Sonnet, Haiku, Opus. Each requires your own Anthropic key via [BYOK](https://docs.neuralconfig.com/neuralrepo/integrations/byok/) - **Workers AI** — GPT-OSS 120B (the default) and Qwen 30B. No key required Your selected model is remembered in `localStorage` across sessions. ### Streaming & Thinking Responses stream token-by-token over a WebSocket connection. A collapsible **"Thought process"** block appears above the response text when extended thinking is active — click it to expand and read the model's reasoning. ### Tool Results Tool calls render as collapsible **ToolResultCards** in the message thread. Each card shows: - A summary line (e.g., "Found 5 ideas for 'machine learning'") - Expandable detail content - Clickable **idea chips** — click a chip to highlight that node on the map and scroll it into view Graph-mutating tool calls (creating ideas, creating connections, semantic layout) animate directly onto the map. See [Artifacts](https://docs.neuralconfig.com/neuralrepo/agent/artifacts/) for full details. ### Selection Context When you have nodes selected on the map, the agent automatically receives the selected idea IDs and titles as additional context. This lets you say things like "summarize these" or "connect these ideas" without specifying them by name. ### Undo The panel header shows an **undo** button when there are reversible agent actions in the current session. Undo reverses what the agent *wrote* — it deletes an idea the agent created, or removes relations it added — and it works one action at a time, newest first. Layout changes are not part of the stack; clear an agent arrangement with the toolbar or by asking the agent to clear it. ## Performance The mind map uses two rendering modes depending on graph size, switched automatically: - **SVG rendering** (up to 200 nodes) — full interactivity, crisp at any zoom level - **Canvas rendering** (more than 200 nodes) — optimized for large graphs, device-pixel-ratio aware The force simulation is D3.js. Charge repulsion and ideal edge length both come from the spread control — tightening it pulls the graph to roughly 60px edges, loosening it pushes past 190px — and the remaining physics (velocity decay, collision padding) are fixed. To group nodes deliberately rather than by physics, use the agent's `arrange_nodes` or `semantic_layout`. ## Next Steps - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): The edges themselves: types, direction, cycle rules, and who created them. - [AI Agent Overview](https://docs.neuralconfig.com/neuralrepo/agent/overview/): The chat panel that drives the map — models, streaming, and graph mode. - [Artifacts & Visualization](https://docs.neuralconfig.com/neuralrepo/agent/artifacts/): What each agent tool draws on the graph, and what the undo button reverses. - [Map API](https://docs.neuralconfig.com/neuralrepo/api/map/): Fetch the same nodes and edges to build your own visualization. --- # Idea Relations Source: https://docs.neuralconfig.com/neuralrepo/organize/relations/ Relations let you express how ideas connect to each other. Every relation has a **source**, a **target**, a **type**, and an optional **note**. Together, they form a directed graph that you can explore in the mind map or via the CLI. ## Relation Types NeuralRepo supports six relation types. Each serves a distinct purpose. | Type | Direction | Meaning | |------|-----------|---------| | `related` | A → B | General connection. A and B are conceptually related. | | `parent` | A → B | Hierarchical. A is the parent of B. Use this to break large ideas into sub-ideas. | | `blocks` | A → B | A blocks progress on B. B cannot move forward until A is resolved. | | `inspires` | A → B | A inspired the creation of B. A seed that grew into something new. | | `duplicate` | A → B | A is a duplicate of B. Yours to create by hand — duplicate detection does not use it. | | `supersedes` | A → B | A replaces B. The newer idea makes the older one obsolete. | **Note:** Relations are directional — `A blocks B` is not the same as `B blocks A`. The source is always the idea performing the action, and the target is the idea being acted upon. ## Creating Relations **Caution:** Creating, editing, and deleting relations requires **Pro**, on every surface. Free accounts can read the graph — the detail panel, the mind map, `nrepo links`, `nrepo graph`, `GET /api/v1/map` — but the write endpoints return `403 pro_required`, the MCP `link_ideas` and `unlink_ideas` tools refuse, and the mind map's editing gestures are disabled. **Web** Open an idea and click **+ Add Relation** in the relations panel. Select the target idea, choose a relation type, and optionally add a note explaining the connection. **CLI** ```bash # Link two ideas with a relation nrepo link <source> <target> --type <type> --note "optional explanation" # Examples nrepo link 10 42 --type blocks --note "Need auth system before dashboard" nrepo link 5 18 --type parent nrepo link 3 7 --type inspires --note "The API idea grew out of this" ``` **API** ```bash curl -X POST https://neuralrepo.com/api/v1/map/relations \ -H "Authorization: Bearer $NREPO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_idea_id": 10, "target_idea_id": 42, "relation_type": "blocks", "note": "Need auth system before dashboard" }' ``` ## Cycle Detection Certain relation types enforce acyclicity to maintain a coherent graph. | Behavior | Relation Types | What happens | |----------|---------------|--------------| | **Hard-blocked** | `blocks`, `parent` | Cycles are rejected outright. You cannot create a relation that would form a loop. | | **Soft-blocked** | `supersedes` | Cycles are rejected unless overridden with `--force`. | | **Not checked** | `related`, `inspires`, `duplicate` | No cycle detection — these types are non-hierarchical. | Cycle checking walks up to 50 hops along edges of the same type, so it catches long chains, not just direct loops. To override a soft-blocked cycle: ```bash # CLI nrepo link 3 7 --type supersedes --force # API POST /api/v1/map/relations?force=true ``` **Note:** A `duplicate` relation is not part of the duplicate-detection workflow — detections are resolved by merging or dismissing, and never produce this relation type. Use it when *you* want to record that two ideas are the same thing without merging them. ## Viewing Relations **CLI** ```bash # List all relations for an idea nrepo links <id> # Explore the graph from a starting idea nrepo graph <id> # Limit depth and filter by type nrepo graph <id> --depth 3 --type blocks,inspires ``` **Web** The idea detail view shows a **Relations** panel listing all incoming and outgoing relations. Click any related idea to navigate to it. For a visual overview, open the **Mind Map** to see ideas as nodes and relations as edges. **API** ```bash # Get the full graph GET /api/v1/map # Response includes nodes and edges { "nodes": [...], "edges": [ { "id": 1, "source": 10, "target": 42, "type": "blocks", "note": "Need auth system before dashboard", "score": null, "created_at": "2026-03-20T14:30:00Z" } ] } ``` ## Auto-Created Relations NeuralRepo creates some relations for you. They are marked as system-created, which the mind map draws fainter and thinner than the ones you make yourself. - **Similarity**: when a newly saved idea scores above the related threshold (default 0.55) against one of its five nearest neighbours, a `related` relation is created — including when the pair also crosses the dedup threshold and becomes a duplicate detection. A pair gets at most one automatic link, and none if it is already linked. - **Your links win**: linking two ideas by hand replaces their automatic `related` link, so the pair shows once. - **Parenting**: giving an idea a `parent_id` creates a `parent` relation from the child to the parent. Changing `parent_id` later deletes the old one and writes the new one. **Caution:** Editing an idea's title or body **recomputes its system-created relations** from the new text, so an automatic link can disappear or a new one appear. Relations you created by hand are untouched, and an automatic link you removed is never added back. To keep a link through rewrites, create it by hand. The thresholds are configurable in your user settings. See [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/) for details. ## What Merging Does to Relations Merging two ideas with `nrepo merge` (or `POST /api/v1/ideas/:id/merge`) **moves the absorbed idea's relations onto the kept idea**, dropping any that would point an idea at itself. Resolving a duplicate detection does not: it archives the matched idea, and archived ideas are filtered out of every relation query, so their links simply stop appearing. ## Removing Relations **CLI** ```bash nrepo unlink <source> <target> ``` **API** ```bash DELETE /api/v1/map/relations/:id ``` **Web** Open the idea detail, find the relation in the Relations panel, and click the remove button. On the mind map, right-click an edge and choose **Remove link**. ## Next Steps - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): See the graph these relations form — edge colours, filters, and focus mode. - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): Where the automatic related links come from, and how to resolve a duplicate. - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): New connections land here, so you can accept or ignore them as they appear. - [nrepo link](https://docs.neuralconfig.com/neuralrepo/cli/link/): Create, force, and batch relations from the terminal. --- # Review Queue Source: https://docs.neuralconfig.com/neuralrepo/organize/review/ **Review** is the one view that tells you what to look at. Instead of scrolling your whole list, you get a short queue of ideas that changed state on their own — a possible duplicate appeared, NeuralRepo connected two of your ideas, or something you captured has sat untouched for weeks — and one or two buttons that clear each item. Open it from **Review** in the sidebar. The nav item carries a count while anything is waiting. ## What Lands in the Queue **Possible duplicates** Pairs that scored above your dedup threshold, **most similar first**, each showing the similarity percentage. **Pro only** — the section is empty on the free plan even though detections are still being recorded. **New connections** Relations created in the **last 7 days**, whether NeuralRepo inferred them from similarity or you made them yourself. Auto-detected ones are labelled as such and show their score. **Gone quiet** Ideas still sitting in `captured` past your staleness threshold — **30 days** by default, configurable between 7 and 180 — with how many days they have been untouched. ## The Actions Each card offers only the moves that make sense for it. | Section | Action | What it does | |---------|--------|--------------| | Possible duplicates | **Merge** | Folds the pair together and marks the detection `merged`. See [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/#merge) for exactly which idea survives | | Possible duplicates | **Not a duplicate** | Marks the detection `dismissed`; the pair never comes back | | New connections | **Open** | Opens the source idea so you can judge the link in context | | New connections | **Seen it** | Hides the card for this session only — nothing is written, and it reappears until the connection ages out of the 7-day window | | Gone quiet | **Keep** | "Still relevant" — bumps the idea's `updated_at`, restarting the staleness clock without changing anything else | | Gone quiet | **Develop** | Moves the idea to `exploring`, which takes it out of the stale queue for good | | Gone quiet | **Archive** | Soft-deletes the idea | **Note:** **Seen it** is the only action that isn't persisted. Everything else writes through the same endpoints as the rest of the app, so a merge or a status change made here shows up everywhere immediately. ## When the Queue Is Empty You get a "Queue clear" panel rather than an empty list. That is the normal state — the queue only fills as ideas age or as background processing finds something, so seeing it empty every day means your capture habit and your triage habit are in balance. ## Free vs Pro | Section | Free | Pro | |---------|------|-----| | Possible duplicates | Hidden | Shown | | New connections | Shown | Shown | | Gone quiet | Shown | Shown | Free accounts get a genuinely useful queue — staleness and new connections are the two sections that need no AI spend. Duplicates are the Pro addition, and because detections are recorded for every account, the backlog is waiting in full if you upgrade later. ## Tuning the Staleness Threshold The **Gone quiet** section and the weekly digest's stale section share one setting, `stale_threshold_days` (7–180, default 30). **Web** **Settings ▸ Notifications** — set the number of days an idea can sit in `captured` before NeuralRepo considers it stale. The same control governs the stale-idea nudge emails. **API** ```bash curl -X PATCH https://neuralrepo.com/api/v1/user/me \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"settings_json": "{\"stale_threshold_days\": 60}"}' ``` **Tip:** A high capture rate and a low threshold make the queue noisy. If Review feels like a chore, raise the threshold to 60 or 90 days before you start archiving things you might still want. ## The Email Version The queue has a mailed counterpart. The **weekly digest** (Sunday, Pro) summarises the week including stale ideas, and a separate **stale-idea nudge** (Monday, all plans, capped to about once a month) points at the same ideas the Gone quiet section lists. Both respect `stale_threshold_days`, and both can be turned off in **Settings ▸ Notifications**. ## Next Steps - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): How pairs are found, what merging really does, and how to tune the thresholds. - [Idea Relations](https://docs.neuralconfig.com/neuralrepo/organize/relations/): What the connections in the queue mean, and which ones NeuralRepo makes itself. - [Status Workflow](https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/): Where 'Develop' moves an idea, and what the other four statuses are for. - [Weekly Digest](https://docs.neuralconfig.com/neuralrepo/integrations/weekly-digest/): The emailed version of the same signals, and how to tune or silence it. --- # Search Source: https://docs.neuralconfig.com/neuralrepo/organize/search/ NeuralRepo offers two search modes that work together to help you find ideas quickly, whether you remember the exact wording or just the general concept. ## Search Modes **Semantic Search** Uses vector similarity powered by **Cloudflare Vectorize** and the **@cf/baai/bge-m3** embedding model. Your query is converted into an embedding and compared against stored idea embeddings. Results are ranked by relevance score and reranked for quality. Searching for "mobile habit tracker" will surface an idea titled "daily routine app for iOS" even though the words do not overlap. **Keyword Search** Uses **SQLite FTS5** full-text search for exact word matching. Fast and precise when you know the specific terms you are looking for. Searching for "React" returns only ideas that literally contain the word "React." **Note:** Free-tier users get **10 semantic searches per calendar month**, counted in UTC and reset on the 1st. When the limit is reached, searches fall back to keyword mode with a notice in the response. Pro users have unlimited semantic search. The counter increments when the search starts, so a semantic search that matches nothing still spends one. ## Default Behavior — and Where the Fallback Actually Is Searching without a mode runs **semantic search**. What happens when semantic search finds nothing above the threshold depends on which surface you asked from, and the difference matters: | Surface | No semantic matches | |---------|--------------------| | `GET /api/v1/ideas/search` | Returns **an empty result set** with `search_type: "semantic"` | | `nrepo search` | Same — reports 0 results | | Web app | Same, until you switch the mode toggle to keyword | | Agent tool `search_ideas` | **Falls back to keyword** automatically (`search_type: "fts_fallback"`) | | MCP `search_ideas` and `code_mode` | **Falls back to keyword** automatically | So the REST API and the CLI do not silently retry — ask for `mode=keyword` when you know the exact wording. The one automatic fallback everywhere is the free-plan quota: once the ten monthly semantic searches are used, every surface returns keyword results — the REST API and the CLI flag it with `semantic_limit_reached: true`, and the MCP tool with a `notice` string. ## Searching **Web** Use the search box in the sidebar and pick a mode. Keyword search offers three matching styles — **any word**, **all words**, or **exact** phrase — and the result header names the mode that answered, so you always know which one you are reading. **CLI** ```bash # Semantic search nrepo search "distributed task queue" # Search: "distributed task queue" (semantic) — 3 results # # #42 exploring ⬡ Background job processing system [backend] (87%) # #18 captured ◎ Message queue architecture (74%) # #7 shipped ◈ Async worker pattern (62%) ``` `nrepo search` has no keyword mode of its own — use the web app or the API for that. **API** ```bash # Default — semantic, no keyword fallback GET /api/v1/ideas/search?q=distributed+task+queue # Force semantic only (identical to the default) GET /api/v1/ideas/search?q=distributed+task+queue&mode=semantic # Force keyword only GET /api/v1/ideas/search?q=distributed+task+queue&mode=keyword # Keyword matching style: or (default), and, exact GET /api/v1/ideas/search?q=task+queue&mode=keyword&fts_mode=and # Override the score threshold for one request GET /api/v1/ideas/search?q=task+queue&min_score=0.45 ``` Response: ```json { "results": [ { "id": 42, "title": "Background job processing system", "body": "...", "score": 0.87, "status": "exploring", "tags": ["backend", "infrastructure"] } ], "search_type": "semantic", "query": "distributed task queue" } ``` **MCP** When connected to Claude AI via MCP, use the `search_ideas` tool: ``` "Search my NeuralRepo for ideas about authentication" ``` Claude will call the `search_ideas` tool and return matching results within the conversation. ## Combining Search with Filters Search results can be narrowed by combining the query with status and tag filters via the API. **API** ```bash GET /api/v1/ideas/search?q=auth&status=building&tag=backend ``` ## Search Threshold The search threshold controls the minimum relevance score for semantic results. Ideas scoring below this threshold are excluded from results. | Setting | Range | Default | Scope | |---------|-------|---------|-------| | `search_threshold` | 0.1 - 0.9 | **0.2** | Your account, every surface | | `min_score` query parameter | 0 - 1 | *(uses `search_threshold`)* | One API request | - **Lower values** (e.g., 0.2, the default) cast a wide net, surfacing more potential matches. Raise it if results feel noisy. - **Higher values** (e.g., 0.7 or 0.8) return fewer results, only very close matches. Configure the threshold in your user settings: ```bash # Via API curl -X PATCH https://neuralrepo.com/api/v1/user/me \ -H "X-API-Key: nrp_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"settings_json": "{\"search_threshold\": 0.5}"}' ``` **Tip:** The default threshold of 0.2 is intentionally permissive so you do not miss relevant results. If searches feel noisy, raise the threshold to 0.5 or higher. ## How Semantic Search Works 1. Your query text is sent to the **@cf/baai/bge-m3** model running on Cloudflare Workers AI. 2. The model generates a vector embedding representing the meaning of your query. 3. The embedding is compared against your stored idea embeddings in **Cloudflare Vectorize**. NeuralRepo asks for roughly three times the number of results you want (at least 20, at most 50) so there is room to filter and rerank. 4. Matches scoring at or below the search threshold are dropped, as are archived ideas. 5. A reranking step reorders what survives, and the top `limit` results are returned. Idea embeddings are generated asynchronously when an idea is created or updated. There may be a brief delay before a brand-new idea appears in semantic search results. Archiving an idea removes its vector immediately, so a deleted idea stops influencing search and duplicate detection right away. ## Next Steps - [Duplicate Detection](https://docs.neuralconfig.com/neuralrepo/organize/duplicates/): The same embeddings, compared against your five nearest ideas. - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): Narrow a search by tag, and see how tags get assigned in the first place. - [Search API](https://docs.neuralconfig.com/neuralrepo/api/search/): Every query parameter and response field for the search endpoint. - [Plans & Pricing](https://docs.neuralconfig.com/neuralrepo/getting-started/pricing/): How the free plan's 10 semantic searches a month are counted. --- # Status Workflow Source: https://docs.neuralconfig.com/neuralrepo/organize/status-workflow/ Every idea in NeuralRepo has a **status** that represents where it is in your workflow. There are five statuses, and an idea is always in exactly one of them. ## The Five Statuses **Captured** A raw idea, just saved. No evaluation or refinement has happened yet. This is the default status for every new idea regardless of how it was captured. **Exploring** You are actively researching or fleshing out this idea. It has caught your attention and you are evaluating its potential, gathering references, or sketching out requirements. **Building** Development is in progress. You have committed to this idea and are actively working on turning it into something real — writing code, designing screens, drafting content. **Shipped** Completed and launched. The idea has been realized and is live, published, or otherwise done. **Shelved** Parked for later. This is not the same as abandoned — a shelved idea is one you may come back to when the timing is right or when priorities shift. **Tip:** Statuses are not strictly sequential. You can move an idea from any status to any other status. Jump straight from **captured** to **building** if you are ready, or move a **shipped** idea back to **exploring** if you want to iterate on it. **Note:** Capture forms only offer the first three — `captured`, `exploring`, and `building` — since an idea you are still writing down has not shipped or been shelved. Every other surface accepts all five. ## Kanban Board The **Board** view is a drag-and-drop kanban board where each column represents a status. Drag an idea card from one column to another to change its status instantly. The kanban board shows idea titles, tags (with custom colors), and a truncated body preview. Click any card to open the full idea detail view. ## Changing Status **Web** - **Kanban board**: Drag the idea card to the target column. - **Idea detail**: Click the status badge and select a new status from the dropdown. **CLI** ```bash # Move a single idea to a new status nrepo move <id> <status> # Examples nrepo move 42 exploring nrepo move 18 shipped ``` **API** ```bash curl -X PATCH https://neuralrepo.com/api/v1/ideas/42 \ -H "Authorization: Bearer $NREPO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"status": "exploring"}' ``` Valid values: `captured`, `exploring`, `building`, `shipped`, `shelved`. ## Bulk Status Changes Move multiple ideas to the same status in a single command. ```bash # Move ideas 1, 2, and 3 to "building" nrepo move building --ids 1,2,3 ``` In the web app, tick the checkbox on each idea in the Ideas list, then choose a status from the bulk toolbar that appears. Bulk moves are applied one idea at a time and reported per idea — a failure on one does not roll back the others — with a maximum of 50 ideas per request. ## Filtering by Status Filter your idea list to show only ideas in a particular status. **Web** Use the status filter in the sidebar, or simply look at the relevant kanban column. **CLI** ```bash # List all ideas with a specific status nrepo log --status exploring # Combine with tag filter nrepo log --status building --tag ai ``` **API** ```bash GET /api/v1/ideas?status=exploring ``` ## Status Reference | Status | Default for new ideas | Typical next step | |--------|----------------------|-------------------| | `captured` | Yes | `exploring` or `shelved` | | `exploring` | No | `building` or `shelved` | | `building` | No | `shipped` or `shelved` | | `shipped` | No | Terminal, or back to `exploring` | | `shelved` | No | `exploring` when revisited | **Note:** There is no "deleted" status. Deleting an idea **archives** it: it disappears from lists, search, and the graph, and its vector is removed so it stops influencing search and duplicate detection — but the row is retained. Shelving is the softer alternative: shelved ideas stay in your repository and remain searchable, and are only skipped when comparing a new idea for duplicates. ## Status Everywhere Else - **Stale ideas** — an idea left in `captured` past your staleness threshold (default 30 days) surfaces in the [Review queue](https://docs.neuralconfig.com/neuralrepo/organize/review/) and in the weekly digest. Moving it on, or pressing **Keep**, resets the clock. - **The mind map** colours every node by status and can filter the graph down to one. - **Auto-created ideas** land in `exploring`, not `captured`: the agent's `synthesize_ideas` and `draft_document` both produce worked-on ideas rather than raw ones. ## Next Steps - [Review Queue](https://docs.neuralconfig.com/neuralrepo/organize/review/): Where ideas that stopped moving come back to your attention. - [Tags](https://docs.neuralconfig.com/neuralrepo/organize/tags/): The other axis of organization — and how bulk tagging works. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): Status as colour: read progress across the whole graph at a glance. - [nrepo move](https://docs.neuralconfig.com/neuralrepo/cli/move/): Single and bulk status changes from the terminal. --- # Tags Source: https://docs.neuralconfig.com/neuralrepo/organize/tags/ Tags are the primary way to categorize and filter ideas in NeuralRepo. Every idea can have up to **20 tags**, and each tag name can be up to **50 characters** long. ## Creating Tags Tags are created on the fly when you assign them to an idea. There is no separate "create tag" step — just start using a tag and it exists. **Web** Open an idea and click the **+ Add Tag** button in the sidebar. Type a tag name and press Enter. The tag is created and assigned immediately. **CLI** ```bash # Add one or more tags to an idea nrepo tag <id> <tags...> # Examples nrepo tag 42 ai machine-learning nrepo tag 18 side-project weekend ``` **API** ```bash curl -X PATCH https://neuralrepo.com/api/v1/ideas/42 \ -H "Authorization: Bearer $NREPO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tags": ["ai", "machine-learning"]}' ``` ## Tag Format Tags should follow these conventions for best results: | Rule | Example | |------|---------| | Lowercase | `machine-learning` not `Machine-Learning` | | Hyphenated for multi-word | `side-project` not `side project` | | Max 50 characters | Enforced by the API | | Max 20 tags per idea | Additional tags are rejected with a `400` error | **Tip:** Consistent tag naming makes filtering more effective. Stick to lowercase, hyphenated names like `mobile-app`, `api-design`, or `revenue-model`. ## Auto-Classification Save an idea with **no** tags and NeuralRepo tags it for you, a few seconds later, as part of the same background pass that generates embeddings and looks for duplicates. Which mechanism runs depends on your plan: | Plan | How it tags | What you get | |------|-------------|--------------| | **Pro** | An AI classifier (IBM Granite on Workers AI) picks from your 10 most relevant neighbour tags and your 5 most-used tags | 1–3 tags, reusing your vocabulary. It may invent one new lowercase, hyphenated tag (max 30 characters) if nothing fits | | **Free** | Tag-borrowing from the ideas it just matched — no AI call, no cost | Up to 3 tags that appear on **two or more** of its nearest neighbours. A new idea with fewer than two related neighbours stays untagged | **Note:** Auto-classification only triggers when an idea has **zero** tags. Provide even one tag and neither mechanism runs — and if you later strip every tag off an idea and edit its text, auto-tagging runs again on the next pass. ## Custom Colors Assign a custom color to any tag to make it visually distinct across the web app — on the kanban board, in search results, and in the mind map. **Web** Click on any tag in the sidebar, then select **Edit Color**. Choose a color from the picker or enter a hex value directly. **API** ```bash curl -X PATCH https://neuralrepo.com/api/v1/tags/1 \ -H "Authorization: Bearer $NREPO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"color": "#6366f1"}' ``` Use the tag's numeric `id` in the path. Colors must be in `#rrggbb` hex format. ## Filtering by Tag Find all ideas with a specific tag across every surface. **Web** Click any tag in the left sidebar to filter the idea list. You can select multiple tags to narrow results further. **CLI** ```bash # List ideas filtered by tag nrepo log --tag ai # Combine with status filter nrepo log --tag ai --status exploring ``` **API** ```bash # Filter ideas by tag GET /api/v1/ideas?tag=ai # Multiple tags (intersection) GET /api/v1/ideas?tag=ai&tag=mobile-app ``` ## Bulk Operations Add or remove a tag from multiple ideas at once using the CLI, or select ideas in the web app and use the bulk toolbar. ```bash # Add a tag to multiple ideas nrepo tag add ai --ids 1,2,3 # Remove a tag from multiple ideas nrepo tag remove deprecated --ids 4,5 ``` **Caution:** Bulk operations are **not atomic**. Each idea is updated on its own and reported on its own — a bad or unknown ID fails alone while every other idea still changes. The CLI prints a ✓ or ✗ per idea and a final count; the API returns `updated`, `errors`, and a per-idea `results` array. A bulk request takes at most **50 ids**. ## Listing Tags with Counts Retrieve all tags in your repository along with the number of ideas each tag is applied to. **API** ```bash GET /api/v1/tags ``` Response: ```json { "tags": [ { "id": 1, "name": "ai", "color": "#6366f1", "idea_count": 14 }, { "id": 2, "name": "mobile-app", "color": null, "idea_count": 8 }, { "id": 3, "name": "side-project", "color": "#f59e0b", "idea_count": 5 } ] } ``` **CLI** Tag listing is not available as a standalone CLI command. Use `nrepo log --tag <name>` to filter ideas by tag, or use the API or web dashboard to see all tags with idea counts. ## Tidying Up Tags accumulate. Two things help: - **Tags with no ideas are deleted automatically.** Whenever a tag is removed from its last idea, the tag itself is cleaned up, so the sidebar never fills with empty labels. - **The AI agent can merge near-duplicate tags** — `ml` and `machine-learning`, `AI` and `ai`. Its `cleanup_tags` tool compares tag embeddings, proposes clusters above 0.85 similarity, and keeps the most-used name in each. It previews by default; you confirm before anything merges. See [Agent Tools](https://docs.neuralconfig.com/neuralrepo/agent/tools/#cleanup_tags). ## Limits | Limit | Value | |-------|-------| | Tags per idea | 20 | | Tag name length | 50 characters | | Ideas per bulk tag request | 50 | | Color format | `#rrggbb` hex | The 20-tag cap is enforced when tags are **submitted**. Merging two ideas unions their tags without re-checking it, so a merged idea can end up carrying more than 20. ## Next Steps - [Search](https://docs.neuralconfig.com/neuralrepo/organize/search/): Combine a tag filter with semantic or keyword search. - [Mind Map](https://docs.neuralconfig.com/neuralrepo/organize/mind-map/): Tags colour the nodes and drive the map's tag filter. - [Agent Tools](https://docs.neuralconfig.com/neuralrepo/agent/tools/): suggest_tags and cleanup_tags — AI help for a messy tag list. - [Tags API](https://docs.neuralconfig.com/neuralrepo/api/tags/): List, rename, recolour, and delete tags programmatically.