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

# Understanding SpoofSense Scores and Decision Thresholds

> How calibrated scores work, how the decision threshold is resolved, and guidance for tuning liveness and deepfake detection accuracy.

Every SpoofSense check returns a `genuine_score` in `[0, 1]` — higher means more likely a genuine, live capture — and applies one rule to produce a binary decision:

```text theme={null}
decision = genuine_score > threshold
```

Understanding how that score is calibrated and how the threshold is chosen gives you the control you need to tune the system for your risk tolerance.

## The score is calibrated

Raw model probabilities shift when models are retrained. SpoofSense remaps every model's output so that **the decision boundary always sits at 0.5**: a threshold you tune today keeps meaning the same thing after a model update, and thresholds are comparable across products.

Model internals — architecture, raw per-class probabilities — are deliberately not exposed.

## Where the threshold comes from

The threshold is resolved in priority order — the first match wins:

1. **Per-request override** — `threshold` (or `thresholds` on unified) in the request body.
2. **Organization default** — set per product in the console (**Dashboard → Settings**).
3. **Platform default** — `0.5`.

The threshold actually applied is echoed back in every response, so your logs always show exactly which rule made the call.

## Tuning guidance

| You want                        | Move the threshold | Trade-off                                              |
| ------------------------------- | ------------------ | ------------------------------------------------------ |
| Catch more spoofs (stricter)    | Up, e.g. `0.6`     | More genuine users rejected (higher false-reject rate) |
| Fewer false rejections (looser) | Down, e.g. `0.4`   | More spoofs slip through                               |

<Tip>
  Start at the default `0.5` and only move after measuring on your own traffic. Small adjustments — 0.05 at a time — make it easier to attribute changes in accept/reject rates.
</Tip>

## Unified detection and per-product thresholds

On `POST /v1/unified_detection`, the overall `decision` is `"real"` only if **every** product's check passes its own threshold. The per-product outcomes are in `checks`:

```json theme={null}
{
  "product": "unified",
  "decision": "spoof",
  "checks": {
    "face_liveness": { "decision": "real",  "genuine_score": 0.91, "threshold": 0.5 },
    "deepfake":      { "decision": "spoof", "genuine_score": 0.31, "threshold": 0.5 }
  },
  "latency_ms": 420,
  "credits_remaining": 997,
  "session_id": { "face_liveness": "lsn_…", "deepfake": "dfs_…" },
  "request_id": "req_…"
}
```

## Thresholds in verification sessions

For [verification sessions](/verification-sessions/overview), thresholds are fixed **server-side at session creation** — the client can never loosen them. Set the threshold when calling `POST /v1/verification_sessions` from your backend; it will be honored when the session is scored.
