# Reading moderation reports

A moderation report is the classification result attached to a completed asset. Always check `status` before reading a score: an unevaluated category is unknown, not safe.

For what each category and qualifier means, see [Category definitions](/docs/guides/moderation-categories). For how reports turn into actions, see [Policies and actions](/docs/guides/moderation-policies).

Audio/video reports can include `media.segments` with `source: "clip"` for native analysis of overlapping chunks. These are approximate evidence ranges. Native audio/video produces parent-category scores without ordinary qualifier scores or a transcript; profiles requiring qualifiers retain the legacy pipeline. See [audio and video limits](/docs/guides/audio-and-video#coverage-and-limits).

## Reading reports safely

Narrow on `status` before accessing `score`:

**python**

```python
import os
from calmlens import CalmLensClient

client = CalmLensClient(
    api_key=os.environ["CALMLENS_API_KEY"],
    project_id=os.environ["CALMLENS_PROJECT_ID"],
)

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

**go**

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    calmlens "github.com/Censkh/CalmLens/packages/go-sdk"
)

func main() {
    client, err := calmlens.NewClient(calmlens.Options{APIKey: os.Getenv("CALMLENS_API_KEY"), ProjectID: os.Getenv("CALMLENS_PROJECT_ID")})
    if err != nil { log.Fatal(err) }
    ctx := context.Background()
    asset, err := client.Assets.Retrieve(ctx, calmlens.AssetIDOptions{AssetID: "your-asset-id"})
    if err != nil { log.Fatal(err) }
    if asset.Report != nil {
        violence, found := asset.Report.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**

```javascript
const violence = asset.report?.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
require 'calmlens'

client = CalmLens::Client.new(api_key: ENV.fetch('CALMLENS_API_KEY'), project_id: ENV.fetch('CALMLENS_PROJECT_ID'))
result = client.assets.retrieve(asset_id: "your-asset-id")
puts result
client.close
```

**rust**

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new(std::env::var("CALMLENS_API_KEY")?, std::env::var("CALMLENS_PROJECT_ID")?)?;
    let result = client.assets().retrieve(AssetId { asset_id: "your-asset-id".into(), ..Default::default() }).await?;
    println!("{result:?}");
    Ok(())
}
```

**java**

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

public class Example {
  public static void main(String[] args) {
    var client = new CalmLensClient(System.getenv("CALMLENS_API_KEY"), System.getenv("CALMLENS_PROJECT_ID"));
    var result = client.assets.retrieve(new AssetId().assetId("your-asset-id"));
    System.out.println(result);
  }
}
```

Every report contains all six category keys. Check each result’s evaluation status before using its score.

## Report shape

Every report contains all six parent categories. Overlapping content-type, context and severity evidence is nested under `qualifiers`. These are not additional top-level categories.

| Top-level field | Meaning |
| --- | --- |
| `processedAt` | Unix timestamp in milliseconds for the completed report. |
| `flagged` | Whether the final [policy](/docs/guides/moderation-policies#actions) decision applied a non-ignored action. It is not a raw classification flag. |
| `score` | Highest evaluated parent-category score. It does not account for configured thresholds. |
| `categories` | Required results for all six parent categories. |
| `labels` | Optional processing labels. Labels do not drive the six policy thresholds. |
| `notes` | Optional processing context. |

## 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 |
| --- | --- |
| `score` | Evidence that the qualifier is present anywhere in the category's qualifying content. **Any** conditions use it. |
| `scopeScore` | Evidence that the qualifier covers all qualifying content in the category. **Only** conditions use it. It never exceeds `score`. |

Direct image classification supplies presence only and omits `scopeScore`; text has separate presence and scope checks. Missing scope cannot establish an Only condition, and a missing qualifier cannot establish either.

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 puts the asset into `error` 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. `report.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, child assets, 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 six categories and selected qualifiers.

```json
{
  "processedAt": 1788364800000,
  "flagged": true,
  "score": 0.91,
  "categories": {
    "sexual": {
      "status": "evaluated",
      "score": 0.02,
      "likelihood": "very-unlikely"
    },
    "violence": {
      "status": "evaluated",
      "score": 0.91,
      "likelihood": "very-likely",
      "qualifiers": {
        "physical-attack": {
          "status": "evaluated",
          "score": 0.98,
          "scopeScore": 0.95,
          "likelihood": "very-likely"
        },
        "historical": {
          "status": "evaluated",
          "score": 0.97,
          "scopeScore": 0.97,
          "likelihood": "very-likely"
        },
        "graphic": {
          "status": "evaluated",
          "score": 0.91,
          "scopeScore": 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"
    }
  }
}
```
