`POST /api/projects/{id}/backlog/scan?dry_run=<bool>` (S3). Auth required; the project must belong to the caller's active workspace (enforced in the service). `dry_run` defaults to `true` (preview — writes nothing). The acting user is the authenticated caller (the service is workspace-scoped, so a foreign project is a 404).
POST
/api/projects/{id}/backlog/scan
const url = 'https://example.com/api/projects/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/backlog/scan';const options = {method: 'POST'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/api/projects/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/backlog/scanParameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”id
required
string format: uuid
Project ID
Query Parameters
Section titled “Query Parameters”dry_run
boolean
Responses
Section titled “Responses”Media typeapplication/json
The scan response: the contract shared with the FE agent. On dry_run=true
no rows are written and new_items previews what WOULD be inserted; on
dry_run=false items are inserted idempotently (ON CONFLICT DO NOTHING).
object
dry_run
required
boolean
existing
required
integer
new
required
integer
new_items
required
Array<object>
One scanned/previewed item, as surfaced on the scan report (the minimal
shape the FE preview needs — the full row lives in backlog_items).
object
external_id
required
string
title
required
string
project_id
required
string format: uuid
source
required
linear | notion | the git-provider name.
string
total_issues
required
integer
Examplegenerated
{ "dry_run": true, "existing": 1, "new": 1, "new_items": [ { "external_id": "example", "title": "example" } ], "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "source": "example", "total_issues": 1}