# Reading classification results

A completed asset's `classification` groups its findings by capability. `classification.moderation` holds category scores, qualifiers, the policy assessment and the decision. Person detection, face detection and labels sit alongside it. Use [Category definitions](/docs/guides/moderation-categories) to interpret the findings and [Policies and actions](/docs/guides/moderation-policies) to understand the decision.

## Reading reports safely

Check each category's `status` before reading its `score`: an unevaluated category is unknown, not safe.

**TypeScript Server SDK**

```typescript
const violence = asset.classification.moderation.categories?.violence;

if (violence?.status === "evaluated" || violence?.status === "partial") {
  console.log(violence.score, violence.likelihood);
} else if (violence) {
  console.log(`Violence was not evaluated: ${violence.reason}`);
}
```

**python**

```python
asset = client.assets.retrieve({"assetId": "your-asset-id"})
moderation = asset["classification"]["moderation"]
violence = moderation.get("categories", {}).get("violence")
if violence and violence["status"] != "not-evaluated":
    print(violence["score"], violence["likelihood"])
elif violence:
    print("Violence was not evaluated:", violence["reason"])
```

**go**

```go
package main

import (
    "context"
    "fmt"
    "log"
    calmlens "github.com/CalmLens/sdks/go"
)

func example(client *calmlens.ServerClient) {
    ctx := context.Background()
    asset, err := client.Assets.Retrieve(ctx, calmlens.AssetIDOptions{AssetID: "your-asset-id"})
    if err != nil { log.Fatal(err) }
    if asset.Classification.Moderation.Categories != nil {
        violence, found := asset.Classification.Moderation.Categories["violence"]
        if found && (violence.Status == "evaluated" || violence.Status == "partial") && violence.Score != nil {
            fmt.Println(*violence.Score, violence.Likelihood)
        } else if found {
            fmt.Println("Violence was not evaluated:", violence.Reason)
        }
    }
}
```

**JavaScript Server SDK**

```javascript
const violence = asset.classification.moderation.categories?.violence;

if (violence?.status === "evaluated" || violence?.status === "partial") {
  console.log(violence.score, violence.likelihood);
} else if (violence) {
  console.log(`Violence was not evaluated: ${violence.reason}`);
}
```

**ruby**

```ruby
result = client.assets.retrieve(asset_id: "your-asset-id")
puts result
```

**rust**

```rust
use calmlens::{models::*, ServerClient};

async fn example(client: &ServerClient) -> Result<(), Box<dyn std::error::Error>> {
    let result = client.assets().retrieve(AssetId { asset_id: "your-asset-id".into(), ..Default::default() }).await?;
    println!("{result:?}");
    Ok(())
}
```

**java**

```java
import com.calmlens.CalmLensServerClient;
import com.calmlens.Models.*;

public class Example {
  public static void example(CalmLensServerClient client) {
    var result = client.assets.retrieve(new AssetId().assetId("your-asset-id"));
    System.out.println(result);
  }
}
```

## Report shape

Every report contains all eight parent categories. Overlapping evidence within a category is nested under `qualifiers`. These are not additional top-level categories. A category disabled in your policy can still have raw findings; see [preset defaults](/docs/guides/moderation-policies#built-in-policies). Older reports show these categories as not assessed rather than inventing a zero score.

The report holds the classification evidence, plus a `policy` section: what your [policy](/docs/guides/moderation-policies#policy-score) made of that evidence when the asset was decided. The action it took is the asset's `classification.moderation.action`, which is `null` when nothing was flagged.

| Field in `classification`          | Meaning                                                                                                                                                              |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                           | `pending`, `processing`, `completed` or `failed`.                                                                                                                    |
| `updatedAt`                        | When the classification state last changed, in Unix milliseconds.                                                                                                    |
| `completedAt`                      | When classification completed, in Unix milliseconds. `null` until completion; unchanged by manual reviews.                                                           |
| `moderation.categories`            | Safety findings for all eight parent categories, once available.                                                                                                     |
| `moderation.score`                 | Highest evaluated category score. It does not account for policy thresholds.                                                                                         |
| `moderation.decision`              | `pending`, `approved` or `rejected`; a reviewer's decision takes precedence.                                                                                         |
| `moderation.action`                | The saved policy action: `flag`, `hide`, `purge` or `null`.                                                                                                          |
| `moderation.policy`                | The saved policy scores, deciding rules, settings snapshots and automatic decision. Changing a policy later does not update these results.                           |
| `moderation.timeline`              | Optional audio/video sampling details and timed category evidence.                                                                                                   |
| `moderation.manualReviews`         | Review history, oldest first. Each entry records the decision, chosen action (approve, hide or purge), note, reviewer, time and whether it was applied to the asset. |
| `personDetection`, `faceDetection` | Optional detections requested through `processingOptions.features`. They do not affect the moderation decision.                                                      |
| `labels`                           | Optional detected content labels. They do not drive policy thresholds.                                                                                               |

OCR text and transcripts live in `content.extractedText`, so their retention follows the source content. OCR safety findings contribute to `classification.moderation.categories`.

For example, a saved category includes the rules needed to explain its decision without loading the current policy:

```json
{
  "score": 0.81,
  "action": "hide",
  "source": { "kind": "restriction", "index": 0 },
  "settings": {
    "threshold": 0.6,
    "categoryAction": "hide",
    "allowances": [{ "name": "Historical context", "when": ["violence-historical"], "action": "allow" }],
    "restrictions": [{ "name": "Graphic detail", "when": ["violence-graphic"], "action": "hide" }]
  }
}
```

Audio/video reports can include `classification.moderation.timeline.segments` with `source: "clip"` when CalmLens evaluates overlapping chunks directly. These are approximate evidence ranges. Direct evaluation produces parent-category scores without qualifier scores or a transcript; policies with allowances or restrictions use the frame/OCR/transcript pipeline instead. See [audio and video limits](/docs/guides/audio-and-video#coverage-and-limits).

## Category and qualifier results

Each parent category nests its evidence under `qualifiers`. Qualifier IDs are category-specific; the current sets and their definitions are listed in [Category definitions](/docs/guides/moderation-categories). Use the documented qualifier IDs for each category.

| Qualifier field | Meaning                                                                                                                                                   |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `someScore`     | Evidence that the qualifier is present in some of the category's qualifying content. See [Some and All](/docs/guides/moderation-categories#some-and-all). |
| `allScore`      | Evidence that the qualifier covers all qualifying content in the category. It never exceeds `someScore`.                                                  |

For categories scoring at least 10%, CalmLens assesses every qualifier regardless of which rules your policy uses. Below that score, separate qualifier checks are not run. Older reports may lack evidence for qualifiers that their policy did not request.

Text and direct image classification both have separate presence and scope checks. Missing scope gives an allowance no coverage, and a missing qualifier gives neither an allowance nor a restriction any evidence.

For how those fields affect a decision, see [Policy score](/docs/guides/moderation-policies#policy-score).

Qualifiers have their own evaluation status and do not change the parent category's score. Their scores are diagnostic evidence, not probabilities of correctness.

## Evaluation status

| Status          | Meaning                                                                                     | Score fields                                                           |
| --------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `evaluated`     | The parent category was directly evaluated. Individual qualifiers may still be unavailable. | Includes `score` and `likelihood`.                                     |
| `partial`       | A narrower signal was evaluated, but not the complete parent category.                      | Includes the strongest available qualifying signal and its likelihood. |
| `not-evaluated` | No usable score exists for this category. Policy thresholds are not applied to it.          | Includes `reason`; omits `score` and `likelihood`.                     |

Qualifiers use `evaluated` or `not-evaluated`; they do not use `partial`.

Possible `not-evaluated` reasons are:

| Reason          | Meaning                                                                                                                                                                                       |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `not-supported` | The processing path used for this asset cannot assess this category or qualifier.                                                                                                             |
| `unavailable`   | Assessment was attempted but produced no usable result. A complete moderation failure normally sets the asset's `classification.status` to `failed` instead of returning a successful report. |

Coverage varies by input type and processing availability, and a parent result does not guarantee that every qualifier was evaluated.

Do not convert `not-evaluated` into a zero score. Zero means the signal was evaluated and received its lowest score; `not-evaluated` means CalmLens has no result.

## Scores and likelihoods

Scores are normalized to the range `0`–`1`. Likelihood labels use these boundaries:

|         Score | Likelihood      |
| ------------: | --------------- |
|       `< 0.2` | `very-unlikely` |
| `0.2`–`< 0.4` | `unlikely`      |
| `0.4`–`< 0.6` | `possible`      |
| `0.6`–`< 0.8` | `likely`        |
|      `>= 0.8` | `very-likely`   |

The parent category score retains the strongest supported category evidence, including directly evaluated narrower checks. Qualifiers do not change this score. `classification.moderation.score` is the maximum evaluated parent score. Scores are normalized into the same shape, but are not calibrated probabilities of correctness.

## OCR and merged image reports

Enable OCR when written content in images matters. See [OCR and image reports](/docs/guides/ocr) for setup, image-owned extracted text, combined scores and usage.

## Audio and video reports

See [Audio and video moderation](/docs/guides/audio-and-video) for supported formats, sampling limits, timelines and usage.

## Example report

This example includes all eight categories, selected qualifiers and the policy section an Education and support project records. Violence is wholly historical, but its graphic detail is a restricted form, so its policy score stays high.

```json
{
  "status": "completed",
  "updatedAt": 1788364800000,
  "completedAt": 1788364800000,
  "moderation": {
    "categories": {
      "sexual": {
        "status": "evaluated",
        "score": 0.02,
        "likelihood": "very-unlikely"
      },
      "violence": {
        "status": "evaluated",
        "score": 0.9,
        "likelihood": "very-likely",
        "qualifiers": {
          "violence-attack": {
            "status": "evaluated",
            "someScore": 0.98,
            "allScore": 0.95,
            "likelihood": "very-likely"
          },
          "violence-historical": {
            "status": "evaluated",
            "someScore": 0.97,
            "allScore": 0.97,
            "likelihood": "very-likely"
          },
          "violence-graphic": {
            "status": "evaluated",
            "someScore": 0.9,
            "allScore": 0.9,
            "likelihood": "very-likely"
          }
        }
      },
      "self-harm": {
        "status": "evaluated",
        "score": 0,
        "likelihood": "very-unlikely"
      },
      "harassment": {
        "status": "evaluated",
        "score": 0.02,
        "likelihood": "very-unlikely"
      },
      "hate": {
        "status": "evaluated",
        "score": 0.73,
        "likelihood": "likely"
      },
      "criminal-activity": {
        "status": "evaluated",
        "score": 0.02,
        "likelihood": "very-unlikely"
      },
      "personal-information": {
        "status": "evaluated",
        "score": 0.03,
        "likelihood": "very-unlikely"
      },
      "profanity": {
        "status": "evaluated",
        "score": 0.04,
        "likelihood": "very-unlikely"
      }
    },
    "score": 0.9,
    "policy": {
      "score": 0.81,
      "categories": {
        "sexual": {
          "score": 0.02,
          "action": "allow"
        },
        "violence": {
          "score": 0.81,
          "action": "hide"
        },
        "self-harm": {
          "score": 0,
          "action": "allow"
        },
        "harassment": {
          "score": 0.02,
          "action": "allow"
        },
        "hate": {
          "score": 0.73,
          "action": "hide"
        },
        "criminal-activity": {
          "score": 0.02,
          "action": "allow"
        }
      }
    },
    "decision": "rejected",
    "action": "hide",
    "manualReviews": []
  }
}
```
