---
title: "Ingest Code Occurrences"
method: POST
path: "/v1/projects/{project_id}/code-occurrences"
tags: ["code-occurrences"]
---

# Ingest Code Occurrences

`POST /v1/projects/{project_id}/code-occurrences`

## Request

- Base URL: `https://api.sonenta.dev`
- URL: `https://api.sonenta.dev/v1/projects/{project_id}/code-occurrences`
- Auth: none declared

Ingest ONE batch of a key-usage run (Developer+).

Lifecycle: omit `scan_id` to OPEN a run (the response mints + echoes the id),
echo it on follow-up batches, send `final=true` on the last one to CLOSE the
run — only the close reconciles `gone` and runs the mass-gone guard. A
`scan_id` that is not the project's current open run is a 422.

Each `ref` is resolved server-side to the BASE-version key_uuid; refs matching
no live base key are counted `unresolved` and echoed (the missing-key signal),
never stored. Re-posting a batch is idempotent. The response ALWAYS carries
`scan_id` when supported — the CLI treats its absence as a pre-contract backend
and must not drop occurrences silently.

## Path parameters

- `project_id` string, required

## Request body

- CodeOccurrencesIngestIn
  - `occurrences` CodeOccurrenceIn[]: One BATCH of key-usage sites. All occurrences of a given (key, file) MUST be in the same batch (chunk by file). The run is authoritative only at `final`.
    - `ref` string, required: Raw usage reference as written in the code, e.g. `common:actions.save` or a NUDE `quality.agent.copied`. The backend resolves it to a base-version key_uuid; send NO uuid. A nude ref (no `:`) binds to the project's DEFAULT namespace, exactly as i18next does at runtime — that is the common case (apps call useTranslation() without a namespace).
    - `file` string, required: Repo-relative path of the usage site.
    - `line` integer, required: 1-indexed line; 0 when the scanner cannot place it.
    - `column` integer, nullable
  - `scan_id` string, nullable: Omit on the FIRST batch to open a run — the response mints and echoes the id. Echo that id on every follow-up batch of the same run.
  - `final` boolean: Set true on the LAST batch (or the only batch of a single-shot run) to close the run: reconcile `gone` and run the mass-gone guard. Non-final batches only upsert.
  - `meta` object, nullable: Free-form run context (tool, version, sha).
  - `confirm_mass_gone` boolean: Set true ONLY when a final that retires most of the project's live occurrences is intended. Without it such a close is refused (422) — the usual cause is a partial checkout.

## Response `200`

Successful Response

- CodeOccurrencesRunOut
  - `scan_id` string, required: ALWAYS present when supported. Absence = pre-contract backend.
  - `status` string, required: `open` (more batches expected) or `final` (run closed).
  - `submitted` integer, required
  - `resolved` integer, required
  - `unresolved` integer, required
  - `unresolved_refs` CodeOccurrencesUnresolved[], required
    - `ref` string, required
    - `file` string, required
    - `reason` string, required: unknown_namespace | unknown_key | no_default_namespace (a nude ref on a project with several namespaces and no `common` — no derivable default)
  - `new` integer, required
  - `persisting` integer, required
  - `gone` integer, required
  - `seen_total_for_scan` integer, required
  - `base_version_uuid` string, nullable
  - `formula` object, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/sonenta/apis/sonenta-api.md) · [All operations](https://skmtc.dev/sonenta/apis/sonenta-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sonenta/apis/sonenta-api/revisions/f853ea49fb05?raw)
