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 (frommeta.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 viainput_commitment) - ×
metafields 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.0becomes1,1e-7becomes1e-7,0.30000000000000004keeps 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 overfp|icwith the published key - →
fingerprint_match- recomputedfp2_over the submitted payload equals the receipt's - →
input_commitment- recomputed over{language, text}with the submitted key material (ic3_: API credential orbk_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) orscore(finite number); NaN/Infinity can never appear (unencodable in JCS) - × Unknown fingerprint prefix - only
fp2_(v2) andfp_(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_commitmentis 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 thebk_-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_idon 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/verifyaccepts either form ininput.key: your API credential (derived server-side, never stored) or thebk_-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-keyand retired public keys remain retrievable via that endpoint'sprevious_keyslist - 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_idrevokedin 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 checkkey_status/trusted(POST /v1/verifyreports 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 viaPOST /v1/verifyindefinitely.
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/eventreports 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 |
|---|---|---|
| 400 | invalid_detection_config | The value is not an sqcfg_* identifier. |
| 403 | version_pinning_not_available | The plan does not include the version-pinning entitlement. |
| 410 | configuration_retired | The pinned configuration reached the unavailable lifecycle state. |
| 422 | configuration_scope_mismatch | The pin was issued for a different analyzer set than the request's. |
| 422 | configuration_unavailable | The ID is unknown or can no longer be resolved. |
| 503 | configuration_suspended | Execution 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.
Verify it yourself
Interactive demo - no API key
Quickstart Guide
First API call in 5 minutes
API Reference
Full endpoint documentation
How Sentiqra Works
Architecture & pipeline
Indicator Dictionary
Browse all 200+ indicators
vs LLM Moderation
Regulatory comparison
Pricing
From Free to Enterprise
Support
Get help from our team
Ready to test the guarantee?
Start with 5,000 free analyzer runs. No credit card required. Run the determinism test yourself.