Skip to content
NeuralRepo
Get Support

Relations & Graph

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.

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.

Terminal window
curl https://neuralrepo.com/api/v1/ideas/137/relations \
-H "X-API-Key: nrp_YOUR_KEY"

Response 200 OK

{
"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.

POST /api/v1/map/relations

Pro only. Accepts a single relation, or up to 50 at once.

FieldTypeRequiredDescription
source_idea_idnumberYesid of the source idea
target_idea_idnumberYesid of the target idea
relation_typestringNorelated (default), parent, blocks, inspires, or supersedes
notestringNoFree text; anything past 500 characters is silently truncated
Terminal window
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"
}'

Response 201 Created

{
"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 scorescore 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.

Send a links array instead of a single body. Up to 50 per call.

Terminal window
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

{
"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"
}
]
}

PATCH /api/v1/map/relations/:id

Pro only. Changes the type, the note, or both.

FieldTypeRequiredDescription
relation_typestringNorelated, parent, blocks, inspires, or supersedes
notestring | nullNoUpdated note; null clears it, over 500 characters truncates
Terminal window
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.

Response 200 OK — the updated relation wrapped in { "relation": {...} }, or 404 if the relation id is not yours.

DELETE /api/v1/map/relations/:id

Pro only.

Terminal window
curl -X DELETE https://neuralrepo.com/api/v1/map/relations/17 \
-H "X-API-Key: nrp_YOUR_KEY"

Response 200 OK

{
"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.

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.

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:

TypesCycle behaviour
blocks, parentHard block — always refused; ?force=true does not help
supersedesSoft block — refused, but ?force=true creates it anyway
related, inspiresNot checked at all — the walk is skipped

The rejection message names the whole chain:

{
"error": "This would create a circular blocking chain: #137 → #96 → #137"
}

For supersedes the same message ends with Use ?force=true to bypass.

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.
Terminal window
# 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"
}'
FieldTypeDescription
idnumberRelation identifier — what PATCH/DELETE take
source_idea_idnumberSource idea id
target_idea_idnumberTarget idea id
relation_typestringrelated, parent, blocks, inspires, supersedes, or duplicate (system only)
scorenumber | nullSimilarity score for system-created relations; null for yours
notestring | nullOptional description, stored truncated at 500 characters
created_bystringuser or system
created_atstringUTC YYYY-MM-DD HH:MM:SS
StatusMeaning
200 OKRead, update, or delete succeeded
201 CreatedRelation created — including bulk calls where every link failed
400 Bad RequestValidation error, self-link, unknown relation type, or a refused cycle
401 UnauthorizedMissing or invalid auth
403 ForbiddenFree plan — writing relations requires Pro
404 Not FoundAn idea id in the request does not exist, or the relation id does not (PATCH only)
409 ConflictA user-created relation already exists between that pair, in that direction