Skip to content
NeuralRepo
Get Support

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.

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.

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.

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

Response 200 OK

{
"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"
}
]
}
FieldMeaning
idThe detection id — this is what the dismiss and merge endpoints take
idea_idThe idea whose capture or edit triggered the detection — normally the newer one
duplicate_of_idThe older idea it matched
idea_body, duplicate_bodyFirst 280 characters only, for previewing
similarity_scoreCosine similarity, 0–1
statusAlways 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 ids.

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.

Terminal window
curl -X POST https://neuralrepo.com/api/v1/ideas/duplicates/7/dismiss \
-H "X-API-Key: nrp_YOUR_KEY"

Response 200 OK

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

POST /api/v1/ideas/duplicates/:id/merge

Folds the two ideas together and marks the detection merged.

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

Aspectduplicates/:id/mergeideas/:id/merge
Survivoridea_id — the newer ideaThe id in the path; you choose
BodyBoth joined with \n\n---\n\nBoth joined with \n\n---\n\n
TagsUnion of bothUnion of both
created_atThe earlier of the twoThe earlier of the two
RelationsNot transferredRepointed onto the survivor
LinksNot transferredNot transferred
Loser’s statusUnchanged, just archivedSet to shelved, then archived
Detection rowMarked mergedUntouched
Response{ "success": true }The surviving idea object

Archiving is permanent, 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 instead, naming the keeper yourself.

Response 200 OK

{
"success": true
}
  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: raise it for fewer, stricter matches.

StatusMeaning
200 OKListed, dismissed, or merged
400 Bad Request:id is not a number
401 UnauthorizedMissing or invalid auth
404 Not FoundDetection not found, not yours, or already dismissed/merged

No endpoint here returns 403 on Free — the list simply comes back empty.