{
    "service": "sint-data",
    "version": "v1",
    "auth": "Bearer token (per-consumer, scoped read/write)",
    "write": [
        "POST /v1/subjects {name, domain?, industry?, hq_country?, employee_count?, external_ids?} -> {subject_id} (upsert by normalized name)",
        "POST /v1/jobs {subject_id, source_ref?, requested_by?, scope?} -> {job_id, revision}",
        "POST /v1/jobs/{job}/findings {findings:[{category,key,label?,value?,value_json?,persona?,confidence?,source_url?,weight?,customer_facing?,source_type?}]} -> upsert (supersedes prior same category+key). customer_facing defaults to TRUE for every finding (Seb, 2026-08-19: SINT output is internal-first and staff decide what leaves the building). clear_reason records `default-allow:seb-2026-08-19`. A writer-supplied customer_facing flag is accepted and ignored. SintClearRule adjudication is retained but inactive; it returns only if a `.clear-gate-on` sentinel is placed beside index.php. source_type in osint|sint|meeting|manual (default sint)",
        "POST /v1/jobs/{job}/sources {sources:[{url,title?,publisher?,retrieved_at?,snippet?}]}",
        "POST /v1/jobs/{job}/org {nodes:[{ref,name,title?,department?,persona?,reports_to_ref?,is_decision_maker?}]}",
        "POST /v1/jobs/{job}/complete",
        "POST /v1/enrich-requests {company, notes, attrs?, opportunity_ref?, requested_by?} -> {request_id}. REQUIRED: company + notes. attrs = OPTIONAL HINTS ({website?,industry?,hq_country?}, producer-varying, often absent) \u2014 industry/hq_country are things the extraction DERIVES from notes, never preconditions. A worker must never fail a row for a missing hint.",
        "POST /v1/enrich-requests/claim {worker_id} -> {request:{...,claim_token}, resumed:bool}. Send worker_id (your WORKER_ID). resumed=true means you already held this row \u2014 finish it, it is not new work. One job in flight per worker. The response carries `source_type` (meeting|sint) derived from opportunity_ref \u2014 use it as the DEFAULT source_type when writing findings instead of hardcoding one; a finding explicitly attributed to a meeting inside the brief may still be tagged meeting. `alps opp:` = seller meeting notes, `alps sint:` = completed research report, `alps split:` inherits its parent.",
        "CONVENTION multi-company brief: one subject = ONE company. If a brief covers several prospects (e.g. a speed-meeting round), do NOT write them all under the brief`s subject_name \u2014 enqueue one child request per identifiable company with opportunity_ref \"alps split:<parent id>\" and an instruction header naming the single company to extract, then finish the PARENT with status=done, findings_written=0 and a note listing the children. The parent`s subject_name may be a venue/event, not a prospect: never create a subject for it. Do not invent placeholder subjects for unresolved people \u2014 record them in the parent`s note.",
        "POST /v1/enrich-requests/{id} {status:done|failed, findings_written?, error?|note?, claim_token} -> {ok}. `note` and `error` are interchangeable (both land in error_message) \u2014 a done row may carry a note; do not expect a `note` key to be honoured under any other name. Send back the claim_token from your claim: mismatch = 409 (the row was reclaimed and belongs to another claim now), match on an already-finished row = 200 idempotent."
    ],
    "read": [
        "GET /v1/subjects?name=|domain= -> [subjects]",
        "GET /v1/subjects/{id} -> subject + counts",
        "GET /v1/subjects/{id}/findings?category=&status=active -> [findings]",
        "GET /v1/subjects/{id}/summary -> findings grouped by category",
        "GET /v1/subjects/{id}/org -> org tree"
    ]
}