We are currently building something exciting and will be launching soon. Registration is temporarily closed. Launching soon — registration temporarily closed.

Integration

Policy decisions & appeals

Sentiqra returns evidence - signals, scores, and execution states. What your product does with that evidence is your policy. This page ships working code for that boundary so you don't implement it from scratch.

The boundary

Every response separates what the analyzer produced from what you do about it. A high score is evidence about content, not a verdict about a user. Two rules keep that boundary honest:

  • Check execution state before acting. A signal only exists when the analyzer actually ran: status completed with assessment signals_detected. A not_applicable or insufficient_content result is not a clean bill - route it explicitly.
  • Actions belong to your policy, not the score. Thresholds, context, and consequences are customer decisions. The same signal can mean "publish" in one product and "queue for review" in another.

Reference architecture

Everything after the response lives in your environment - your dataset, your policy, your appeals log:

POST /v1/analyze                      -->  response (signals + states + receipt)
sentiqra-policy.py evaluate           -->  decision {action, matched rules, review}
    analysis + policy + context            your code executes the action
appeal-open / appeal-resolve          -->  append-only appeals record

The evaluator and appeals tool run locally against saved files. No case content is sent to Sentiqra to produce a decision or process an appeal.

A versioned policy file

Policy is data, not code: a JSON document you own and version. Rules name the signal codes they act on, the score thresholds you chose, and any contextual facts your system supplies:

{
  "schema": "sentiqra/policy/v1",
  "version": "policy-2026-09",
  "rules": [
    {
      "id": "threat-repeat-offender",
      "when": {
        "analyzer": "trust-safety",
        "signal": "threats_intimidation",
        "min_score": 0.9,
        "context": {"repeat_offender": {"eq": true}}
      },
      "then": {"action": "queue_review", "review": "human_required",
               "reasons": ["high_confidence_threat", "repeat_offender"]}
    },
    {
      "id": "insufficient-content",
      "when": {"analyzer": "trust-safety", "applicability": "insufficient_content"},
      "then": {"action": "no_action", "review": "none",
               "reasons": ["input_not_analyzable"]}
    }
  ],
  "default": {"action": "allow", "review": "none",
              "reasons": ["no_rule_matched"]}
}

Rules are evaluated in document order - list most severe first. Signal conditions only evaluate against analyzers that completed with detected signals; a rule that names status, applicability, or assessment targets that execution state instead. Context conditions compare against facts you pass via --context; a missing fact fails the condition rather than being guessed.

The decision record

{
  "schema": "sentiqra/decision/v1",
  "action": "queue_review",
  "policy_version": "policy-2026-09",
  "matched_rule_ids": ["threat-repeat-offender"],
  "review": "human_required",
  "reasons": ["rule:threat-repeat-offender", "high_confidence_threat",
              "repeat_offender", "signal:threats_intimidation:threat_direct"],
  "evidence": {
    "configuration": "<configuration-alias>",
    "fingerprint": "fp2_…",
    "analyzers": {"trust-safety": {"status": "completed",
                  "applicability": "analyzable",
                  "assessment": "signals_detected"}}
  }
}

The record answers an auditor's four questions: what happened (action), which rules fired (matched_rule_ids), whether a human must review (review), and which analysis it came from (evidence: the configuration alias and response fingerprint). Evaluation is deterministic - the same response, policy, and context always produce the same decision.

Appeals without rewriting history

An appeal record links the original receipt, the policy version, and the action taken - then records the reviewer's outcome and correction in a separate block. The original is never edited; a SHA-256 record_hash over it lets you prove that:

sentiqra-policy.py appeal-open \
    --decision decision.json --receipt receipt.json \
    --reason "author disputes classification" --out appeal.json

sentiqra-policy.py appeal-resolve \
    --appeal appeal.json --outcome overturned \
    --correction correction.json --reviewer rev-7

sentiqra-policy.py appeal-verify --appeal appeal.json   # original intact?

Outcomes are upheld, overturned, or modified. A resolved appeal cannot be re-resolved - corrections are append-only.

Appeals correct individual decisions - they do not measure production quality. Appeals and complaints are a self-selected intake channel: cases nobody appeals are not "correct", and a pile of overturned appeals says something about the appealed slice only. For representative measurement, sample across all traffic strata with sentiqra-audit.py (see the Production Guide).

Pair this with receipts and deterministic replay: the receipt reproduces the analysis, the decision record reproduces the policy outcome, and the appeal record shows what changed on review. When you need signed evidence that a specific analysis-and-action pair was bound at a point in time, mint an event record and store its event_id in the appeal's decision block - a repeated fingerprint alone never proves a unique transaction.

What "reasons" explain - and what they don't

A decision's reasons explain the policy: which rules matched and which signals satisfied them. They are not an explanation of the analyzer's internal reasoning. If you surface explanations to end users, describe them as policy reasons ("your message matched our review policy for threatening content"), not as a faithful account of how the model scored the text.

Get the toolkit

The reference evaluator is a single dependency-free Python file:

curl -O https://sentiqra.tech/eval-toolkit/sentiqra-policy.py
python3 sentiqra-policy.py sample-policy > policy.json   # starter policy
python3 sentiqra-policy.py evaluate --analysis resp.json --policy policy.json --context ctx.json

Related: Understanding signals for the fields these rules match on, and the Production Guide for execution states, pinning, and partial results.