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

Developer Docs

Determinism Guarantee

Same normalized input + same pinned configuration → same canonical analysis — and the output profile is itself a reproducibility dimension: the full equality is same accepted input + same pinned configuration + same output profile → same canonical result. This page documents exactly how that works and what it means for your compliance posture.

What "deterministic" means here

A deterministic system produces a byte-identical analysis payload for identical input under the same pinned configuration and output profile - not "similar" or "statistically consistent." Volatile operational metadata (request IDs, timestamps, latency) may differ; the detection result cannot. Sentiqra guarantees this by construction: pinned configurations use a deterministic analysis path with no generative model, no stochastic sampling, and no model state that changes between requests.

Every successful detection response includes a verification.fingerprint field - a truncated SHA-256 hash of the RFC 8785 canonical serialization of the analysis payload (analyzers, outcomes, signals, language, and configuration). Send the same input twice under the same pinned configuration and the same output profile, and both responses carry the same fingerprint. (The emitted signal rows differ between the full.v1 and reduced.v1 profiles - banded scores and a capped signal count are different canonical payloads - so a receipt's fingerprint is profile-specific.) It is independently reproducible by anyone, at any time, without trusting Sentiqra.

Receipt specification (versioned)

Specification versions

Receipts are versioned. The version is identified by the commitment prefix (ic3_ vs ic2_ vs ic_) and echoed in verification.version. Version 3 is the current spec; earlier versions remain verifiable for receipts issued under them.

Version Prefixes Canonicalization Payload fields Status
3 fp2_ / ic3_ RFC 8785 (JCS) analyzers, configuration, language, outcomes, signals Current - all new receipts
2 fp2_ / ic2_ RFC 8785 (JCS) analyzers, configuration, language, outcomes, signals Superseded - verify via POST /v1/verify
1 fp_ / ic_ Server-side canonical form (legacy) configuration, language, signals Legacy - verify via POST /v1/verify

v3 changes only the input commitment: it keys off an HKDF-derived binding key rather than the raw API credential, so historical verification no longer depends on retaining an authentication secret (see input binding). The fingerprint payload and signature rule are unchanged - a v2 and v3 receipt over the same canonical payload carry the same fp2_. v2 and v1 receipts are not reissued - submit them to POST /v1/verify, which accepts all versions.

1. Canonical payload (v2)

The fingerprint covers the analysis decision only - the requested analyzer set, each analyzer's completion outcome, the emitted signals, the language, and the configuration. The submitted text is deliberately excluded (see input binding below), as is volatile operational metadata.

Fields hashed (exact set - anything else is rejected)

  • → analyzers - the requested analyzer slugs, sorted lexicographically before hashing (array order is not significant)
  • → configuration - the scoped configuration alias that produced the result (e.g. sqcfg_ZZW77NM9)
  • → language - the analysis language
  • → outcomes - map of analyzer slug → completed | failed | overloaded | not_applicable, one entry per requested analyzer (from meta.analyzers)
  • → signals - the signal objects exactly as emitted in the response (every field of every row, in response order)

Excluded from fingerprint

  • × text - the submitted input (bound separately via input_commitment)
  • × meta fields other than analyzer outcomes, usage - latency and billing metadata
  • × verification - the receipt itself (contains the fingerprint; circular dependency)
  • × request_id, latency, timestamps - volatile operational metadata

Because outcomes is authenticated, an empty signals array is unambiguous: {"trust-safety":"completed"} with no signals fingerprints differently from {"trust-safety":"failed"} - "completed with no signals" can never be confused with "never completed." Scores are authenticated fields, not derived: the emitted score (and any confidence band in reduced responses) is hashed verbatim, so tampering with a score fails fingerprint verification.

Every 2xx analysis response carries a receipt, including a response where every requested analyzer reported not_applicable - the outcomes map binds each analyzer's status, so "valid request, nothing applicable ran" is itself a signed, replayable result distinct from any execution.

2. Serialization - RFC 8785 (JCS)

The payload is serialized to a single canonical byte sequence per RFC 8785, JSON Canonicalization Scheme. Any conformant JCS implementation in any language produces identical bytes:

  • → Object keys sorted lexicographically by UTF-16 code units; arrays keep their order
  • → No insignificant whitespace; strings in UTF-8 with minimal escaping (only \b \t \n \f \r \" \\ and control characters < 0x20 escaped)
  • → Numbers encoded per ECMAScript Number::toString - 1.0 becomes 1, 1e-7 becomes 1e-7, 0.30000000000000004 keeps its shortest round-trip form
// Example hashed payload (v2) - canonical bytes actually hashed:
{"analyzers":["trust-safety"],"configuration":"sqcfg_ZZW77NM9","language":"en","outcomes":{"trust-safety":"completed"},"signals":[{"analyzer":"trust-safety","code":"insults_derogation","label":"Insults & Derogation","score":0.86,"level":"category"},{"analyzer":"trust-safety","code":"insult","label":"Insult","score":0.99,"level":"label","parent":"insults_derogation"},{"analyzer":"trust-safety","code":"belittling","label":"Belittling","score":0.99,"level":"label","parent":"insults_derogation"}]}

3. Digest, commitment, signature

  • → Fingerprint = "fp2_" + hex(SHA-256(canonical_payload)[0:16]) - 16-byte truncation, 32 lowercase hex chars. Identical in v2 and v3 over the same payload.
  • → Binding key (v3) = "bk_" + hex(HKDF-SHA256(IKM = UTF-8(API credential), salt = absent, info = "sentiqra/input-binding/v3", L = 32)) - derived once, purpose-separated, kept in your vault. It verifies input commitments but can never authenticate requests.
  • → Input commitment (v3) = "ic3_" + hex(HMAC-SHA256(key = binding_key, JCS({"language":lang,"text":text}))[0:16]) - same truncation as v2, keyed by the derived binding key.
  • → binding_key_id (v3) = "bk_" + hex(SHA-256(binding_key)[0:8]) - the opaque identifier on the receipt; selects the right vault entry without revealing which credential derived it.
  • → Signature = "sig_ed25519_" + hex(Ed25519_sign(ASCII(fingerprint + "|" + input_commitment))) - the signed message is the two values joined by a literal |; independently verifiable offline with the published public key.
Canonical result payload (v2+)  { analyzers[], configuration, language, outcomes{}, signals[] }
                               - the detection decision, never the text
            │
            │  RFC 8785 (JCS) canonical JSON → SHA-256, truncated to 16 bytes
            ▼
verification.fingerprint        fp2_…   Result identity - recompute to prove the result is unchanged

API credential ──HKDF-SHA256 (info "sentiqra/input-binding/v3", L=32)──▶ binding key  bk_…
                               - purpose-separated: verifies commitments, cannot authenticate

Canonical input payload    { language, text }   - bound separately, keyed by the binding key
            │
            │  RFC 8785 (JCS) → HMAC-SHA256 (binding key), truncated to 16 bytes
            ▼
verification.input_commitment   ic3_…   Input binding - only the binding-key holder can
                                       confirm which input a receipt was issued for

            { fingerprint | input_commitment }   - ASCII concatenation, '|' separator
            │
            │  Ed25519 (Sentiqra-held private key; public key published)
            ▼
verification.signature          sig_ed25519_…  Authenticity - proves Sentiqra issued this
                                       receipt; verify fully offline with the public key from
                                       GET /v1/verify/public-key (no API key needed)

verification.key_id             edk_…    Which published public key signed this receipt

verification.binding_key_id     bk_…     Which derived binding key bound the input (v3)

verification.version            3        Receipt spec version (1-2 = legacy receipts)

verification.configuration      sqcfg_…  Pinned configuration ID - replay this exact version to reproduce

Together these fields are the verification receipt. The fingerprint deliberately excludes the submitted text, so a receipt cannot be used to test candidate inputs offline. Timestamps, request IDs, and latency are operational metadata - excluded from the fingerprint by design; that also means the fingerprint proves result identity, not a unique transaction or its time - identical inputs yield identical fingerprints. For event evidence, mint a separately versioned event record via POST /v1/event and verify it via POST /v1/verify/event. The v3 binding key is HKDF-derived from the API credential, so verification secrets no longer couple to credential retention - revoke the credential, keep the bk_ key in your vault. Receipts issued under spec v1/v2 (fp_/ic_/ic2_ prefixes) remain verifiable via POST /v1/verify.

Complete worked example - v3 vector from the published set (values are complete, signed with the public development key):

// Canonical payload bytes (JCS):
{"analyzers":["trust-safety"],"configuration":"sqcfg_TESTVCT0","language":"en","outcomes":{"trust-safety":"completed"},"signals":[{"analyzer":"trust-safety","label":"Insults & Derogation","score":0.93,"type":"insults_derogation"}]}

fingerprint      = fp2_b6e445c291ed0a4ab6a89b854f4adfcb
binding_key      = bk_720b80b4cfb315b880c5d8fb4b9d3b4bed13e74086ff8b7c50f65b33aaa46264  // HKDF("sq_live_vector_key")
binding_key_id   = bk_bc54a32479185739
input_commitment = ic3_e4b7fb90768764c6ccc3a1d635062c27  // text "you are a worthless idiot"
signed message   = "fp2_b6e445c291ed0a4ab6a89b854f4adfcb|ic3_e4b7fb90768764c6ccc3a1d635062c27"
signature        = sig_ed25519_409782bb75344f91ef78dac56cfffc23c8d2b1cdf3d78c7111b8bf70457816318aede14948c06ef10f1c91e5f5f7e323d98e700f512048712f6ba4fe27d54401
key_id           = edk_56475aa7  // development key - production receipts carry the live key_id

All seven vectors (v3 and preserved v2: partial failure, empty signals, not_applicable outcome, non-ASCII input, score edge encodings) are published in receipt-test-vectors.json and embedded in the standalone verifier - a single-file WebCrypto implementation you can download and run offline to cross-check your own implementation.

4. Malformed-data rejection

Verification rejects a payload rather than silently normalizing it - normalization would let a tampered payload still match. POST /v1/verify reports each check independently:

  • → signature_valid - Ed25519 verification over fp|ic with the published key
  • → fingerprint_match - recomputed fp2_ over the submitted payload equals the receipt's
  • → input_commitment - recomputed over {language, text} with the submitted key material (ic3_: API credential or bk_ binding key; ic2_/ic_: raw credential)

Rejected as malformed (no partial verification)

  • × Any top-level field outside {analyzers, configuration, language, outcomes, signals}
  • × Empty or non-string analyzer entries; outcome values outside completed|failed|overloaded|not_applicable
  • × Signal rows missing label (string) or score (finite number); NaN/Infinity can never appear (unencodable in JCS)
  • × Unknown fingerprint prefix - only fp2_ (v2) and fp_ (v1) are recognized

Signal fields beyond the mandatory pair (analyzer, code, level, parent, confidence) are hashed, not dropped - extra fields change the fingerprint, so appended data cannot hide tampering.

Input binding and receipt privacy

A receipt that hashed the raw submitted text into a public fingerprint would create a privacy problem: anyone holding a receipt could test candidate messages offline until the hash matched - a practical confirmation channel for short texts, common abuse phrases, or templated support messages. Sentiqra therefore separates result integrity from input binding:

  • → The fingerprint binds the result, not the input. It covers the analyzer set, outcomes, configuration, language, and signal set. Anyone can verify that a result is intact and deterministic; no one can use it to guess what you analyzed.
  • → The input commitment binds the input, keyed by a purpose-separated secret. Under spec v3, input_commitment is HMAC-SHA256 over the JCS-canonical {language, text} payload keyed by a binding key derived from your API credential via HKDF - not by the credential itself. Only the binding-key holder can recompute it, so only you can confirm which input a receipt was issued for.
  • → The signature binds both together. Ed25519 over fingerprint and input commitment jointly - verifiable offline with the published public key, and the two cannot be mixed across receipts.

The binding key (v3) - separating verification from authentication

Earlier spec versions keyed the commitment with your raw API credential, which coupled historical verification to keeping that credential around forever. v3 derives dedicated key material instead:

  • → binding_key = HKDF-SHA256(IKM = UTF-8(API credential), salt = absent, info = "sentiqra/input-binding/v3", L = 32) - derive it once, store the bk_-encoded value in your vault. HKDF's fixed versioned purpose string keeps this material cryptographically separate from every other use of the credential.
  • → A binding key can verify input commitments - it cannot authenticate requests, issue analyses, or spend quota. You may rotate or revoke the API credential at any time and keep only the binding key.
  • → It is still secret material: whoever holds it can confirm whether a stored input matches a receipt. Treat it like any verification secret - vault it, scope access to auditors, rotate by re-deriving from a new credential.
  • → binding_key_id on each v3 receipt is the opaque identifier of the key that bound it - pick the matching vault entry by id, no credential needed.
  • → POST /v1/verify accepts either form in input.key: your API credential (derived server-side, never stored) or the bk_-encoded binding key. The standalone verifier derives it entirely client-side - the credential never leaves your browser.

What a receipt holder can and cannot infer

  • → Can: verify the receipt was issued by Sentiqra - fully offline using the published Ed25519 public key (GET /v1/verify/public-key), confirm the result payload is untampered, and pin the configuration.
  • × Cannot: recover or confirm the submitted text - unless they also hold the binding key (v3) or the API credential (v2 receipts).
  • × Cannot: treat a repeated fingerprint as evidence a unique transaction happened. Fingerprints are deterministic by design - identical inputs produce identical fingerprints, so a match proves result identity only. For "an event occurred," mint an event record (see below).
  • × Cannot: treat the receipt as proof that an allegation or classification is true. A receipt records what Sentiqra produced for a given input and configuration - evidence of the analysis, not evidence of fact. Whether the analysis was correct is a separate, evaluable question.

To prove to an auditor that a specific input produced a receipt, reveal the stored input and recompute the commitment together - the auditor never needs your key material if you perform the computation. Receipts issued before this scheme carry HMAC-SHA256 signatures; POST /v1/verify accepts all forms.

Key lifecycle - rotation, compromise, legacy

  • → Rotating or revoking an API credential never invalidates its receipts. The signature and fingerprint checks need nothing of yours; the commitment check needs the bk_ binding key derived from the credential that issued them. Derive and vault it before revocation - a revoked credential cannot authenticate new requests but its receipts keep verifying.
  • → Signing-key rotation is handled by key_id. Each receipt records the signing key that produced it. When Sentiqra rotates, the new public key publishes at /v1/verify/public-key and retired public keys remain retrievable via that endpoint's previous_keys list - public keys are never secret, so historical receipts verify offline indefinitely.
  • → Signing-key compromise publishes a revocation, not silence. If a signing key is compromised, Sentiqra marks its key_id revoked in the published key document and rotates to a fresh key. A revoked key's signatures still check out mathematically - that is exactly why verifiers must check key_status/trusted (POST /v1/verify reports both separately): a revoked receipt is authentic-looking but must not be trusted.
  • → What a protected key does NOT prove. The private key never leaves the signing service, and every mint is bound to an execution the service actually ran (an internal exec1_ attestation, not a "sign these bytes" endpoint). But a compromised authorized component could still request misleading signatures over work it really executed - the attestation binds signing to execution; it cannot make the execution honest. Schema validation alone does not solve that.
  • → Credential compromise: revoke the credential - it stops authenticating immediately. Receipts it issued remain valid evidence (they were signed by Sentiqra, not by the credential); anyone who also stole your binding key can only confirm inputs against your receipts, nothing more. Re-derive a fresh binding key from the new credential going forward.
  • → Account closure works the same way. Cancelling your subscription revokes API credentials and ends paid inference - it does not invalidate receipts you already hold. An auditor needs only the receipt, the result, the published public-key document, and the standalone verifier (plus your input and bk_ binding key if input commitment must be confirmed). No live API credential is ever part of the evidence package; see Terms §9.
  • → Legacy receipts: v1 (ic_) and v2 (ic2_) commitments key off the raw credential - verifying them still requires that credential or a record of it. Migrate verification material to v3 going forward; old receipts verify via POST /v1/verify indefinitely.

Same canonical analysis ≠ identical receipt

The fp2_ fingerprint identifies the canonical analysis - same payload, same fingerprint, forever. The receipt around it can still differ: a different credential produces a different binding key and ic3_, a rotated signing key produces a different signature and key_id. Two receipts for the same canonical analysis are equally valid evidence of the same result - changing authentication or signing keys changes the receipt, never the analysis it attests to.

What changes the canonical result

The fingerprint covers the emitted analysis - so anything that changes the emitted payload changes it: a different configuration ID, a different language or analyzer set, a different per-analyzer outcome (a retry that flips failed → completed), and a different output profile. Moving between free and paid tiers switches the profile: reduced.v1 emits banded scores, a confidence field, and at most 5 signals - a different canonical payload and therefore a different fingerprint for the same input. That is intentional: the receipt attests to exactly the result the caller received. Changing your credential, plan label, or signing key alone never changes the fingerprint; changing the plan tier may change which profile the response is shaped under.

Your continuity package

Proving a historical result should not depend on Sentiqra continuing to operate. Retain, per result you may need to defend:

  • → The result payload and its complete receipt (version, configuration, fingerprint, input_commitment, signature, key_id, binding_key_id) - the receipt is only verifiable as a whole.
  • → The original input you submitted (where your own retention policy permits) - receipts bind the input, and replay or input-commitment checks require it.
  • → A snapshot of the public-key document from GET /v1/verify/public-key - it carries the status of every key, current and retired.
  • → The standalone verifier (/verify-receipt.html) - a static file that verifies receipts entirely in your browser, no network call to Sentiqra.
  • → The input + your bk_ binding key only if a third party must confirm the input commitment.
  • → Your own policy and input-preparation versions - the receipt proves the analysis Sentiqra returned; which customer-side pipeline step produced that input is your record to keep.

Receipt verification and continued inference are separate things: the evidence above keeps verifying your results even if the API is unreachable, your subscription has ended, or the configuration that produced them is suspended or retired. New analysis and historical replay are what require a running service.

Offline signature verification

The signing public key is published at GET /v1/verify/public-key and reproduced below for the current key (key_id in each receipt selects the key after rotation):

// GET /v1/verify/public-key (values below are illustrative - fetch live)
{
  "algorithm": "ed25519",
  "key_id": "edk_af4f218e",
  "public_key": "b697a3c4aca5cbba3ac724f2abba75ca29781e490f7e6d8367320632de0f899b",
  "key_status": "active",
  "signed_payload": "fingerprint + '|' + input_commitment",
  "signature_encoding": "sig_ed25519_",
  "receipt_versions": {
    "3": "fp2_/ic3_ - v2 canonical payload; commitment keyed by HKDF binding key",
    "2": "fp2_/ic2_ - RFC 8785 canonical payload {analyzers, configuration, language, outcomes, signals}",
    "1": "fp_/ic_ - legacy payload {configuration, language, signals}"
  },
  "binding_key_derivation": {
    "kdf": "HKDF-SHA256 (RFC 5869)", "ikm": "UTF-8 API credential",
    "salt": "absent", "info": "sentiqra/input-binding/v3",
    "length": "32 bytes", "encoding": "bk_<64 hex>"
  },
  "previous_keys": [
    {"key_id": "edk_9d31f02b", "public_key": "c4…", "status": "retired"}
  ]
}

This document is the trust anchor - a receipt never is. Fetch it yourself from the service's own HTTPS origin and record when. A receipt's key_id may only select among the keys published here - it can never introduce one: an unpublished key_id is unknown, never trusted. Statuses: active signs new receipts; retired still verifies the historical receipts it really signed; revoked is a compromise notice - the signature math still checks but the receipt must not be trusted.

Offline verification has a defined boundary. Offline you know signature math and the key status as of the moment you fetched this document - nothing newer. A verifier that has not fetched recently cannot honestly claim there is no compromise notice; it must report status as snapshot-dated or unknown, never "safe." A manually pasted public key yields mathematical verification only and is never a trust anchor - the standalone verifier labels it accordingly.

Verifying a stored receipt takes four lines with any Ed25519 library:

# Python (cryptography) — fully offline, no Sentiqra call
key  = Ed25519PublicKey.from_public_bytes(bytes.fromhex(public_key))
sig  = bytes.fromhex(receipt["signature"].removeprefix("sig_ed25519_"))
msg  = (receipt["fingerprint"] + "|" + receipt["input_commitment"]).encode()
key.verify(sig, msg)  # raises InvalidSignature on any tampering

Event records: separating "repeatable result" from "an event occurred"

The fingerprint intentionally excludes timestamps and request IDs — that is what makes it stable. But a stable fingerprint is result identity, not event evidence: an identical fingerprint on two occasions proves the same input produced the same result under the same configuration and output profile. It does not prove a unique transaction, a submission time, or any action taken afterward.

When you need "this specific analysis was bound to an event," mint an event record — a separately versioned (sentiqra/event/v1), signed object issued on demand:

POST /v1/event
{
  "receipt":  { "fingerprint": "fp2_…", "input_commitment": "ic3_…",
                "signature": "sig_ed25519_…", "key_id": "…", "configuration": "…" },
  "scope":    "tenant:acme/support-queue",     
  "customer": { "policy_version": "policy-2026-09",
                "action": "queue_review", "reference": "case-1234" }
}

The minted record carries a fresh event_id (random per issuance — identical receipts mint distinct IDs), the bound receipt, your scope, issued_at, an opaque issued_to marker derived from your credential, a digest over the signed core, and an Ed25519 signature from the same key family as receipts. Minting only binds authentic receipts — a forged fingerprint or signature is rejected.

  • → The customer block is declared, not attested. Your policy version and action are signed into the record — proving what you asserted at issuance — but Sentiqra does not observe actions performed in your systems.
  • → issued_at is signer-supplied. It records when Sentiqra issued the binding. For independent proof-of-existence by a particular time, timestamp the record's digest with an RFC 3161 timestamp authority — a TSA's signed token is a different assurance than the issuer's own clock.
  • → POST /v1/verify/event reports the four dimensions separately: result integrity (bound receipt signature), event identity (record signature + unique event ID), input binding (caller-keyed commitment), and timestamp assurance (none / signer-asserted / RFC 3161 with digest-match).
curl -O https://sentiqra.tech/eval-toolkit/sentiqra-event.py
python3 sentiqra-event.py mint --receipt receipt.json --scope "tenant:acme/queue-7" --customer ctx.json
python3 sentiqra-event.py timestamp --record event.json --tsa <rfc3161-tsa-url>   # openssl ts
python3 sentiqra-event.py verify --record event.json --cafile tsa-ca.pem

What to compare across vendors

Whether another system offers an equivalent guarantee is a contract question, not an architecture verdict. Compare supported configuration pinning, historical replay availability, output semantics, verification tooling, and lifecycle guarantees - not only output format:

Configuration pinning

Can you pin the complete analysis behavior to an immutable identifier - and does the vendor commit to output identity under it? Pinning a prompt or model name constrains inputs to the model; it does not by itself commit to identical output.

Historical replay availability

Can you re-run last quarter's input against the exact configuration that produced the original result? Check the vendor's documented deprecation windows - pinned versions are often available only for a limited period.

Output semantics

Do output identifiers carry stable, versioned meaning tied to a documented taxonomy - or labels whose interpretation depends on the prompt and model revision that produced them?

Verification tooling

Does the vendor return a reproducible result identity - a fingerprint or hash over the analysis payload - plus a signature you can verify offline, or a response you can only store?

Lifecycle guarantees

Is there a published support policy covering how long a pinned version stays available, what notice precedes changes, and whether fixes create new versions rather than silently altering existing ones?

Why byte-identical output is rare in generative pipelines

Two documented properties make byte-identity hard for generative systems to contract for: inference results can vary across hardware and library versions in large serving fleets, and sampling remains stochastic even at temperature 0. Generated JSON can be canonicalized like any other JSON - the open question is whether each call emits the same JSON to canonicalize. Some providers reduce variance with seed parameters, dated model snapshots, or system fingerprints; none of these alone is a published commitment to byte-identical output. These are considerations to verify per vendor contract, not impossibilities - any vendor could write the guarantee down, and the questions above are how you check whether they have.

Immutable configuration pinning

Every successful analysis response includes a verification.configuration ID (e.g., sqcfg_ZZW77NM9) alongside the fingerprint. The configuration ID uniquely identifies the complete analysis behavior required to reproduce a result - an opaque, immutable analysis artifact. What a configuration contains internally is not part of the public contract; what it guarantees is that the same input under the same ID produces the same result. A fingerprint is only meaningful together with the configuration that generated it.

When Sentiqra ships an updated configuration (new indicators, improved detection quality, expanded language coverage), a new configuration ID is published. Old results remain reproducible by pinning the original configuration ID while that configuration remains supported under the published support policy or your contract. Configurations are immutable - bug fixes create new versions, never silently change existing ones. This means:

  • → Detection decisions can be reproduced exactly months later - while the configuration that produced them remains in a supported lifecycle state.
  • → Audit logs reference a specific configuration ID, so reviewers can verify decisions against the complete analysis configuration that was active at the time.
  • → Regulatory submissions can cite the configuration ID used for each analysis, providing full traceability.

Requesting a pinned configuration (detection_config)

Send detection_config to pin the configuration a request runs against. The value is an opaque sqcfg_* identifier copied from verification.configuration in a previous response, or from GET /v1/status for the current configuration of each analyzer.

A configuration ID is scoped to an analyzer set: a single analyzer slug, or the canonical sorted set a multi-analyzer request produced (the composite scope in its receipt). A pin is valid only for the exact analyzer set in the request - submitting a single-analyzer ID for a two-analyzer request, or a different analyzer's ID, is a scope mismatch. Output profile (full/reduced) and language do not change the configuration.

When detection_config is omitted the request runs the current configuration for the requested analyzer set. Omission never selects a historical configuration and never carries a previous request's pin forward.

Configuration lifecycle

State Requests Meaning
current accepted The configuration each analyzer runs today. Its alias appears in every receipt and per domain in GET /v1/status. Requests that pin it execute normally.
supported historical accepted A previously current configuration still within its published support window. Requests that pin it reproduce the original canonical result for identical input. No longer the default - requests without a pin run the current configuration.
deprecated accepted A supported historical configuration with an announced retirement date. Requests still execute and reproduce results; callers should migrate pins before the state becomes unavailable.
unavailable rejected A configuration outside its support window. Requests that pin it fail with a documented error; the current configuration is never substituted.

The current configuration is immutable while it is current. Configurations that rotate out remain supported historical for the window published in the changelog entry that introduced their successor; on Enterprise agreements the support window is a contractual commitment for the life of the contract - subject to the security-suspension rule below, which can pause execution (never verification) of any configuration while it applies.

Replay an analysis using its original input and supported configuration ID. Configuration availability follows the published support policy or your contract.

Security suspension

A configuration pins LOGICAL behavior, not a permanently running build. When a dependency, runtime, or deployment component serving a configuration becomes unsafe, a confirmed security_vulnerability incident may suspend execution of the affected scope: pinned and unpinned requests that would execute it fail with configuration_suspended; nothing is substituted and no different detector answers in its place. Execution resumes only after the patched build passes the compatibility attestation path (reproducibility report proving the configuration's promised behavior is unchanged), or the configuration is retired through the documented lifecycle with notice. Where neither is immediately possible, a narrowly-scoped replay arrangement - an isolated deployment approved per engagement - may serve historical replay while the suspended build stays out of the general path. Receipt verification is independent of execution: a suspended or retired configuration's receipts still verify against the published key set - /v1/verify performs cryptographic verification, never re-execution, so availability of one never depends on the other.

Pinning errors

A rejected pin fails the request before any analyzer run executes and is never billed. The current configuration is never silently substituted.

Status Code When
400invalid_detection_configThe value is not an sqcfg_* identifier.
403version_pinning_not_availableThe plan does not include the version-pinning entitlement.
410configuration_retiredThe pinned configuration reached the unavailable lifecycle state.
422configuration_scope_mismatchThe pin was issued for a different analyzer set than the request's.
422configuration_unavailableThe ID is unknown or can no longer be resolved.
503configuration_suspendedExecution of the configuration is suspended by a confirmed quality or security incident. Verification of its receipts is unaffected. Pin a supported historical configuration outside the incident scope, or retry after restoration.

What a configuration ID actually pins

A configuration ID identifies a complete version of analysis behavior - not a model version alone. Reproducibility depends on every element that can change a result, so the ID is backed by private release evidence covering all of it; what those elements are is not part of the public contract. What the ID guarantees publicly:

  • → Equality condition: the same accepted input under the same configuration ID, language, analyzer set, and output profile produces the same canonical result while the configuration is in a supported lifecycle state.
  • → Immutability: a change to any covered element produces a new configuration ID - never an edit to an existing one.
  • → Explicit failure: an unsupported, retired, unknown, out-of-scope, or suspended pin fails the request with a documented error - a different configuration is never silently substituted (see the lifecycle and error table above).

The backing record is an operational artifact, not a public document - it exists so that "the same ID" provably means the same behavior. A request either executes the supported configuration the caller specified or returns a documented error; there is no third outcome.

Runtime enforcement - fail closed, not fail silent

A deployment serves a configuration only under an ID whose behavior it can reproduce exactly. If the behavior the runtime would produce does not match what the deployed configuration's ID promises - because an artifact, a dependency, or the execution environment moved outside the validated scope - the runtime refuses to serve that configuration rather than silently substituting behavior under an existing ID. A runtime or library upgrade may keep an existing configuration ID only after identical behavior is demonstrated within the promised execution scope; otherwise it is served under a new ID.

Reproducibility is additionally evidenced by a test matrix run per deployment environment: repeat calls, segmented inputs, concurrent execution, restarts, boundary-adjacent scores, and tied rankings are checked for byte-identical canonical output. The reports are retained as release evidence - they record scope, environment, and results so the guarantee is measured, not assumed.

Scope of the guarantee

The byte-identical promise applies within the configuration's stated execution scope:

  • → Covered: the canonical analysis payload - the fields the fingerprint hashes - on validated platforms and runtimes.
  • × Not covered: volatile operational metadata (request IDs, timestamps, latency, ordering of unrelated response metadata), and environments outside the validated scope. Byte-identity across platforms is claimed only where a reproducibility report records it.

A configuration pins behavior, not a permanently running build. If a dependency, runtime, or deployment component serving a configuration becomes unsafe, execution of the affected scope can be suspended under a confirmed security incident - requests fail explicitly with configuration_suspended and no different detector answers in its place. Execution resumes only after the patched build proves identical behavior through the compatibility-attestation path; verification of existing receipts is cryptographic and never depends on the suspended execution path.

Verify it yourself

The determinism guarantee is not a claim - it's a testable property. You can verify it in 30 seconds:

# 1. Make a detection call
curl -s -X POST https://sentiqra.tech/v1/analyze \
  -H "Authorization: Bearer sq_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"text": "you are a worthless idiot", "language": "en", "analyzers": ["trust-safety"]}' \
  | jq -r '.verification.fingerprint'

# Output (illustrative): fp2_a3f8e2c1d9b47f6e8a2c5d1b7f3e9a4

# 2. Run it again -- same input, same hash
curl -s -X POST https://sentiqra.tech/v1/analyze \
  -H "Authorization: Bearer sq_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"text": "you are a worthless idiot", "language": "en", "analyzers": ["trust-safety"]}' \
  | jq -r '.verification.fingerprint'

# Output (illustrative): fp2_a3f8e2c1d9b47f6e8a2c5d1b7f3e9a4
# ^ identical under the same pinned configuration and output profile.

Or use the interactive verification page - no API key required. The same loop, run against any other analyzer you use, is the full comparison methodology.

What this means for compliance

EU AI Act

Deterministic output, no probabilistic inference, no training on user data, and no model drift are properties that may be relevant to how a deployment is assessed under the AI Act. Risk analysis depends on intended purpose and use - Sentiqra provides reproducible evidence, not an analysis decision.

DSA & GDPR

Every moderation decision is reproducible while its configuration remains supported under the published support policy or your contract. When a user appeals, you can re-run the exact detection and show the identical hash. Where explainability obligations apply (e.g. GDPR Art. 22), determinism can provide supporting evidence by construction.

MAR & MiFID II

Financial surveillance requires audit trails that survive regulatory review. Immutable configuration pinning means a detection from 2024 can be reproduced in 2026 with exactly the same analysis behavior while the configuration remains supported under the published support policy or your contract.

Internal audit

No more "the model said X but we can't show you why." Every decision has a result fingerprint, a configuration ID, and a caller-held input commitment. Your audit team can recompute fingerprints and replay decisions without trusting Sentiqra's infrastructure; receipt authenticity is confirmable at any time via the public /v1/verify endpoint.

Ready to test the guarantee?

Start with 5,000 free analyzer runs. No credit card required. Run the determinism test yourself.