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.
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-4-20250514", "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.