> ## Documentation Index
> Fetch the complete documentation index at: https://developers.argosidentity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reading analysis results

> The order and the rules for interpreting a GET /analyses/:analysisId response — the extractedData envelope, why fields are empty, verdict values, playbook freezing, and the audit log.

[`GET /analyses/:analysisId`](/en/omni/api-reference/get-analysis) returns more than 30 fields. This page is not about field definitions — it is about **the order to read them in and how to interpret them**. For the field list and types, see the API reference; for terminology, see [core concepts](/en/omni/getting-started/core-concepts).

## The order to read in

<Steps>
  <Step title="Check status">
    Do not go further unless it is `completed`. On `failed`, record `error.code` and `error.message` and check the item statuses.
  </Step>

  <Step title="Check outputFormatting.status">
    `extractedData` follows the output-schema structure only when `systemMetadata.outputFormatting.status` is `formatted` or `validation_failed`. On `skipped` or `fallback_raw` there is no envelope (see below).
  </Step>

  <Step title="Branch on verificationStatus">
    `verified` → approve automatically, `pending_review` → review queue, `rejected` → reject. The detailed verdicts come after that.
  </Step>

  <Step title="Read extractedData and details[]">
    Values are in the envelope's `value`, the reason is in `reasoning`, and the cause of an empty field is the `reasonCode` in `details[]`.
  </Step>

  <Step title="Go to agentAuditLog if you need to">
    Which step decided what, on what grounds, and whether the external engine calls succeeded — that is all in the audit log.
  </Step>
</Steps>

## The `extractedData` envelope

Every **leaf field** of the output schema carries an object with three keys instead of a bare value. Intermediate objects and array structures follow your schema exactly.

| Key          | Type                          | Meaning                                                                                         |
| ------------ | ----------------------------- | ----------------------------------------------------------------------------------------------- |
| `value`      | The type your schema declared | The extracted value. `null` when it could not be obtained                                       |
| `reasoning`  | `string \| null`              | Why the value came out this way. `null` when no reason could be supplied                        |
| `sourceStep` | `number \| null`              | The `agentAuditLog.steps[].step` of the step that filled it in. `null` when it cannot be traced |

```json theme={null}
{
  "extractedData": {
    "company": {
      "legal_name": {
        "value": "ACME TRADING LLC",
        "reasoning": "The legal name on the certificate of good standing matches the account holder on the bank verification letter.",
        "sourceStep": 1
      },
      "state_file_number": {
        "value": null,
        "reasoning": "The state file number was not included in the extraction result.",
        "sourceStep": 1
      }
    },
    "ubo_register": [
      { "name": { "value": "John Carter", "reasoning": "Row 1 of the beneficial ownership certification", "sourceStep": 6 },
        "share_pct": { "value": 60, "reasoning": "Row 1 of the beneficial ownership certification", "sourceStep": 6 } }
    ]
  }
}
```

The rules:

* A field that got no value still **holds its place**, as `value: null`. The field list in `extractedData` always matches the output schema.
* When a parent object itself was not produced, the paths beneath it are not created. In that case `details[]` carries one entry for the parent with `reasonCode: "PARENT_MISSING"`.
* `reasoning` is stored at the moment the analysis completes. Reading the analysis again never changes it.
* `sourceStep` is an execution order number, so it can differ from the next analysis onward if you edit the workflow. To point at a step reliably, use `agentAuditLog.steps[].work_id`.
* `0` and `false` are valid values. Emptiness means `null` / `""` / `[]` / `{}`.

### When there is no envelope

| Condition                         | How to tell                                                                               | Shape of `extractedData`                                                       |
| --------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| The workflow has no output schema | `outputSchema === null` and `outputFormatting.status === "skipped"`                       | The raw result keyed by action name (the same structure as `rawActionResults`) |
| Output schema assembly failed     | `outputFormatting.status === "fallback_raw"`, with the reason in `outputFormatting.error` | Same as above                                                                  |

`fallback_raw` is the most common reason for "every field in the output schema looks empty". Branch on `status` before you look for values by schema key. `validation_failed` means assembly succeeded but some entries do not validate against the schema — the envelope is normal, and the mismatches are listed in `ajvErrors[]`.

<Note>
  Analyses that completed before 2026-08-14 have no `outputFormatting`. For those, decide whether there is an envelope by checking whether `outputSchema` is `null`.
</Note>

## Why a field is empty

A field that received no value stays in the envelope as `value: null`, and a `category: "JSON_OUTPUT"` entry is added to `details[]`, lowering `outputCompleteness` in the Analysis Score.

```json theme={null}
{
  "category": "JSON_OUTPUT",
  "type": "OUTPUT_MISSING",
  "target": "company.state_file_number",
  "reasonCode": "NOT_IN_FINAL_OUTPUT",
  "message": "The legal name and the registered agent were confirmed on the certificate, but no state file number entry was found.",
  "stepId": 1
}
```

| `reasonCode`          | Meaning                                                             |
| --------------------- | ------------------------------------------------------------------- |
| `PARENT_MISSING`      | The parent is absent or empty, so the child was not produced either |
| `EMPTY_VALUE`         | The key exists but the value is `null` / `""` / `[]` / `{}`         |
| `NOT_IN_FINAL_OUTPUT` | The key itself is absent from the assembled final output            |
| `TOOL_ERROR`          | The responsible step's external engine call was recorded as failed  |

* `reasonCode` is an **extensible enum**. Handle a value outside the table without erroring. It is a separate axis from `type` (a fixed set of six, such as `OUTPUT_MISSING` and `OUTPUT_EMPTY`).
* `reasonCode`, `message`, and `stepId` appear only on `JSON_OUTPUT` entries, never on `STEP` or `TOOL` entries.
* `message` is the grounds the responsible **step** recorded, while the envelope's `reasoning` gives the reason at the **field** level. The two sentences can differ, so read `reasoning` first for field-level context.
* `target` is a real instance path. Arrays are expanded by index, one per element (`ubo_register[0].name`). An empty array, or a missing array key, counts as a single `ubo_register` entry.
* Omni does not re-examine the original document. It never claims "the value was not in the document"; the strongest statement it makes is "it was not included in the extraction result" (`NOT_IN_FINAL_OUTPUT`).

### When no reason can be supplied

| Situation                                                    | `reasoning`                 | `reasonCode` in `details[]`        |
| ------------------------------------------------------------ | --------------------------- | ---------------------------------- |
| No output schema, or `fallback_raw`                          | There is no envelope at all | None                               |
| Analysis completed before reasoning was introduced (2026-08) | `null` on every field       | None                               |
| Only the step mapping failed                                 | Normal                      | Normal, but `stepId` may be `null` |

`reasoning: null` does not mean "there were no grounds" — it means "no reason could be supplied". Reasons are not generated retroactively for past analyses.

### Do not use extractionStatus

`extractionStatus` is a **deprecated** field kept for backward compatibility. It works at the level of top-level schema keys (`extracted` / `missing`), so it cannot distinguish nested fields or array elements. New integrations should use the envelope's `value` together with `details[]`. Its emptiness rule matches the envelope (`0` and `false` count as `extracted`). For an analysis with no output schema, it returns a map keyed by the top-level keys of `extractedData`.

## Reading the verdict values

The response holds three levels of similarly named verdicts, each with a different value set.

| Level                                   | Field                                                                                                                                                                           | Values                                               |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| The whole analysis                      | `verificationStatus`                                                                                                                                                            | `verified` / `pending_review` / `rejected`           |
| A playbook action                       | `agentAuditLog.steps[].status`, `agentAuditLog.summary.overall_decision`, `rawActionResults.{action}.verificationStatus`, `systemMetadata.workflowHistory[].verificationStatus` | `passed` / `needs_review` / `failed`                 |
| A status field inside the output schema | `extractedData.{...}`                                                                                                                                                           | Whatever your workflow defined. Not a system verdict |

The top-level verdict aggregates the action results. One `failed` makes it `rejected`, all `passed` makes it `verified`, and anything else is `pending_review`. `summary.overall_decision` is the same aggregate, so `passed` → `verified`, `needs_review` → `pending_review`, and `failed` → `rejected`. `findings[].result` is that same action result mapped for the UI (`needs_review` → `warning`).

### `confidence` vs `confidenceScore`

|                    | `confidence`                                                    | `confidenceScore`                                                                    |
| ------------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Type               | `object` (`score` 0–100, `level`, `components`)                 | `number` 0.0–1.0, or `null`                                                          |
| Meaning            | **Analysis Score** — how complete the output is                 | The agent's confidence in the verdict                                                |
| How it is computed | 40% step execution + 20% step quality + 40% output completeness | On `verified`, the average confidence of the passed actions; otherwise a fixed value |

A high `confidenceScore` can accompany a low `confidence.score`. The classic case is every step passing while the output schema comes back empty. Put `confidence.score` and `details[]` alongside `verificationStatus` in your auto-approval conditions. The weights and level bands are in [core concepts](/en/omni/getting-started/core-concepts#analysis-score).

## Which playbook produced this verdict

An analysis freezes the playbook as it was at run time and returns it as `playbookSnapshot`. Editing the workflow afterwards leaves the grounds of past analyses untouched.

| `snapshotStatus` | Meaning                           | `playbookSnapshot` · `playbookVersion` · `capturedAt`                   |
| ---------------- | --------------------------------- | ----------------------------------------------------------------------- |
| `frozen`         | A snapshot from run time exists   | Based on the snapshot                                                   |
| `legacy`         | Analyzed before snapshots existed | Substituted with the current playbook body; version and time are `null` |
| `missing`        | No snapshot remains               | Same as above                                                           |

If you keep the grounds for audit purposes, store `playbookSnapshot` from analyses marked `frozen`. `outputSchema` is frozen at run time in the same way.

## Reading the audit log

`agentAuditLog` is the step-by-step execution record. Unlike the top-level fields, it uses **snake\_case** (`work_id`, `executed_at`, `duration_ms`).

* Identifying a step: `step` is the execution order and `work_id` is the fixed ID of the playbook action. It corresponds to `workflowActions[].workId` from [`GET /workflows/:workflowId`](/en/omni/api-reference/get-workflow).
* Step verdicts: `status` (`passed` / `needs_review` / `failed`) and `reasoning` are what the Summary tab shows as the reasoning line in the dashboard. `rule` is the instruction that step carried out.
* External engine calls: judge failure from `success` and `error_code` in `mcpcalls[]`. There is no HTTP status code. `fallback_to_rag: true` means it fell back to document search after the engine failed.
* To see the same calls organized by action, use `rawActionResults.{action}.mcpResults[]`. That side uses **camelCase** (`durationMs`, `errorCode`, `fallbackToRag`).
* A step that never ran does not appear in `systemMetadata.workflowHistory[]` either. The full step definitions are in `workflowActions` on the workflow response, and an unexecuted step is recorded in `details[]` as `STEP_NOT_EXECUTED`.

## A pattern for downstream handling

```python theme={null}
a = get_analysis(analysis_id)
if a["status"] != "completed":
    return retry_later()

fmt = a.get("systemMetadata", {}).get("outputFormatting")
raw_mode = fmt is None and a["outputSchema"] is None or (fmt and fmt["status"] in ("skipped", "fallback_raw"))

if a["verificationStatus"] == "verified" and a["confidence"]["score"] >= 80 and not raw_mode:
    approve(a["extractedData"])
elif a["verificationStatus"] == "rejected":
    reject(reason=[s for s in a["agentAuditLog"]["steps"] if s["status"] == "failed"])
else:
    route_to_reviewer(a, missing=[d for d in a["details"] if d["category"] == "JSON_OUTPUT"])
```

* Showing a reviewer the `target`, `reasonCode`, and `message` from `details[]` alongside the envelope's `reasoning` makes the cause much faster to find.
* Re-analyzing the same profile after improving the items issues a new `analysisId`. The earlier result is kept, and you can compare them with the run selector in the dashboard.
* When you need a report a person can read, get the PDF from [`GET /analyses/:analysisId/report`](/en/omni/api-reference/get-report).
