Ideas
Ideas are the core resource. Every endpoint on this page addresses an idea by its database
id, never by the #N display number.
List Ideas
Section titled “List Ideas”GET /api/v1/ideas
Returns unarchived ideas, newest first (created_at descending).
Query Parameters
Section titled “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 "https://neuralrepo.com/api/v1/ideas?status=exploring&limit=10" \ -H "X-API-Key: nrp_YOUR_KEY"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
{ "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
Section titled “Create Idea”POST /api/v1/ideas
Request Body
Section titled “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 |
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" }'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
{ "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
Section titled “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
{ "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
Section titled “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
{ "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.
Update Idea
Section titled “Update Idea”PATCH /api/v1/ideas/:id
All fields are optional. Only provided fields are updated.
Request Body
Section titled “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.
Response 200 OK — the updated idea, with its tags.
Bulk Update Ideas
Section titled “Bulk Update Ideas”PATCH /api/v1/ideas/bulk
Update status and/or tags for up to 50 ideas in one request.
Request Body
Section titled “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.
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"] }'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
{ "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
Section titled “Delete Idea”DELETE /api/v1/ideas/:id
Archives the idea by setting is_archived = 1, then deletes its vector from the search index.
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
Section titled “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 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. An MCP token without ideas:write gets Insufficient scope. |
404 | The id is not yours or does not exist |
List Archived Ideas
Section titled “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.
{ "ideas": [ { "id": 391, "number": 42, "title": "Rewrite auth flow", "updated_at": "2026-09-20 14:02:11" } ], "count": 1}Permanently Delete Archived Ideas
Section titled “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) |
Response 200 OK:
{ "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
Section titled “Merge Ideas”POST /api/v1/ideas/:id/merge
Folds another idea into this one. The idea in the path (keep_id) survives.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
absorb_id | number | Yes | id of the idea to absorb |
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.
Develop Idea
Section titled “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.
Requires a BYOK provider key. Storing one is itself a Pro feature, so in practice this endpoint is Pro-only.
curl -X POST https://neuralrepo.com/api/v1/ideas/137/develop \ -H "X-API-Key: nrp_YOUR_KEY"Response 200 OK
{ "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 message>", "provider": "anthropic" } |
| Idea id not found | 404 | { "error": "Idea not found" } |
Status Codes
Section titled “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.