Understanding Sentiqra signals
Every analysis returns a flat list of structured signals. Each signal is a piece of machine-readable evidence - not a verdict.
The anatomy of a signal
{
"analyzer": "trust-safety",
"code": "insult",
"label": "Insult",
"score": 0.99,
"level": "label",
"parent": "insults_derogation"
}
| Field | Meaning |
|---|---|
| analyzer | Which analyzer produced the signal (e.g. trust-safety). A stable identifier. |
| code | The stable, machine-readable identifier. Safe to use in policy logic - stable in spelling and in meaning (see signal versioning). |
| label | The human-readable name. For display only - do not branch on it. |
| score | Signal-match strength, 0.0–1.0 - see score semantics below. Precision depends on the output profile. |
| confidence | Optional. Present only in the reduced.v1 output profile - the band name whose representative value score then carries. |
| level | category (a broad mechanism) or label (specific evidence). |
| parent | For label-level signals, the code of the category it belongs to. Absent on category-level signals. |
Category vs label
Category-level signals describe a broader mechanism; label-level signals provide more specific evidence nested under a category via parent.
{ "code": "insults_derogation", "level": "category", "score": 0.86 }
{ "code": "insult", "level": "label", "score": 0.99, "parent": "insults_derogation" }
{ "code": "belittling", "level": "label", "score": 0.99, "parent": "insults_derogation" }
Use the category for coarse routing and the labels for fine-grained policy.
What a score does - and does not - mean
A score expresses signal-match strength under a particular analyzer configuration. It is not a probability that the signal or a downstream action is correct. Validate action thresholds on representative labeled data.
- •A high score means the text is a strong match for the signal under that analyzer configuration - not that acting on it is correct.
- •Scores are not comparable across labels or analyzers unless you validate them on your own labeled traffic.
- •Always validate action thresholds against representative labeled data and your policy.
Output profiles
Two versioned output profiles intentionally expose different score precision. A profile's field set and semantics never change in place - a different shape ships as a new profile name and version.
| Output profile | Applies to | score | confidence |
|---|---|---|---|
| full.v1 | All non-free entitlement tiers | Continuous value in [0, 1] | Absent |
| reduced.v1 | Anonymous traffic (playgrounds, demos) and free-tier API keys | Band's representative value (at most 5 signals) | Present - band name |
Under reduced.v1, confidence names the band and score carries that band's representative value:
| confidence | Returned score | Band boundary (public) |
|---|---|---|
| very_high | 0.9 | ≥ 0.85 |
| high | 0.7 | 0.65 – < 0.85 |
| medium | 0.5 | 0.45 – < 0.65 |
| low | 0.3 | 0.00 – < 0.45 |
Signal absence
A missing signal means the analyzer did not detect it above threshold - it does not prove the content is safe. Treat absence as "not detected," never as "cleared." Check meta.analyzers[].assessment to distinguish a completed no_signals run from an abstained run (validated off-domain rejection), and applicability/status for inputs that never executed at all (not_applicable: unsupported language or insufficient content). Full conditions table →
Multiple signals
One input can produce many signals across categories and analyzers. Consume the whole list - the combination is often the meaningful evidence.
Stable codes, reproducible output
Signal code values are stable identifiers - in spelling and in meaning. A code keeps its definition for its lifetime: a materially changed meaning is never silently loaded onto an existing code. It ships as a new code with its own introduction date, while the old code is marked deprecated with a named successor and a migration note, then retired once no supported configuration emits it. Retired records are never deleted, so historical integrations can always resolve what a code meant.
Combined with a pinned configuration and fingerprint, the same input always produces the same canonical signals under the same output profile - so your policy logic and audit trail stay valid over time.
| Version stream | What it pins |
|---|---|
| API contract | Request/response schema and field semantics (registry_version). |
| configuration | The deployed detection configuration per analyzer set - the sqcfg_* alias in every receipt, pinnable via detection_config. |
| taxonomy semantics | What each code means - per-analyzer taxonomy_version; every label's definition, exclusions, parent, introduction date, and deprecation status are recorded in the label registry. |
| your policy | Your own mapping of codes to actions. Owned and versioned by you - we never migrate it. |
Before moving a pin, run your own evaluation set against both configurations with sentiqra-compare.py (in the evaluation toolkit). It runs locally, reports added/removed signals and - with your policy map - changed downstream actions. A pinned integration is never silently migrated: a pin replays the pinned configuration or fails; the current configuration is never substituted.