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

Production

From successful API call to reliable production system.

The first request is easy. This guide covers what changes when you run Sentiqra inside a real pipeline.

Failure behavior

Sentiqra documents exactly what happens when something goes wrong - including whether you were billed.

Situation HTTP Engine called? Billed? Customer action
Invalid JSON400NoNoFix request
Unsupported analyzer400NoNoFix analyzer
Language no requested analyzer covers400 unsupported_languageNoNoCheck the analyzer's language matrix
Analyzer does not cover the declared language200 partialNot that analyzerNoRoute on not_applicable — do not treat as a negative finding
No analyzable letters (emoji-only, numeric-only, symbols)422 / 200 partialNoNoSkip analysis; the input carries no signal
Analyzer not entitled403NoNoChange entitlement
Rate limit429NoNoBack off
One of two analyzers fails200 partialPartiallySuccessful runs onlyX-Retry-Failed child / route
In-flight concurrency cap (per-key fair share or fleet pool)503 detector_unavailableNoNoRetry-After included - retry with backoff; shed load is free
All analyzers unavailable503No successful runNoRetry
Valid idempotent replay200No - recorded response returnedNo new chargeUse response
Replay with different body409 idempotency_conflictNoNoUse a fresh key
X-Retry-Failed child retry200 retry_childFailed analyzers onlyNewly-completed runs onlyUse merged response

Partial execution

When you run two analyzers, one can succeed while the other fails. The request still returns 200 - check meta.analyzers per-analyzer status.

"meta": {
  "analyzers": [
    { "name": "trust-safety",  "status": "completed", "latency_ms": 412, "signal_count": 3, "billed": true },
    { "name": "cybersecurity", "status": "failed",    "latency_ms": 30001, "signal_count": 0, "billed": false,
      "error": "analyzer timeout" }
  ]
},
"usage": { "analyzer_runs": 1 }

Rule

A completed analyzer's result is usable. A failed analyzer's result is unknown - do not treat unknown as safe.

Policy example

If the secondary analyzer is optional, continue with the primary result. If it is safety-critical, retry or route to review.

"No signals" is not "safe" — and not every empty result is an analysis

Three independent dimensions tell you what a response means: status (did it execute), applicability (could this input produce a meaningful result), and assessment (what a completed run established). Route on all three — never on whether the signals array is empty.

Condition Where reported What it means Safe to infer
signals_detectedmeta.analyzers[].assessmentAt least one signal met the reporting criteria.Signals were observed — not proof of the underlying condition.
no_signalsmeta.analyzers[].assessmentThe analyzer completed; nothing crossed the documented thresholds.Nothing was detected — not proof of safety or absence.
abstainedmeta.analyzers[].assessmentA validated rejection rule found the input off-domain for this analyzer.The analyzer declined to report — route like no_signals, not like a verdict.
insufficient_contentmeta.analyzers[].applicability (status not_applicable); 422 on single-analyzer endpointsNo analyzable letters — emoji-only, numeric-only, punctuation-only, symbol-only.Nothing ran. The input carries no analyzable signal.
unsupported_languagemeta.analyzers[].applicability (status not_applicable); 400 when no requested analyzer covers the codeThe analyzer has no coverage for the declared language.Nothing ran — no finding either way. Check the per-analyzer language matrix.
language_fallbackmeta.analyzers[].applicability + effective_languageThe analyzer ran on its English coverage because the declared language is not a dedicated coverage language for this analyzer.Signals are real but coverage is shallower — validate thresholds before acting on them.
  • •Language is declared, not detected. Sentiqra analyzes text under the language you declare (or the documented English default — reported as language_source: "default"). Mixed-language text is analyzed under the single declared language; an incorrect declaration produces a valid result against the wrong coverage. Declare accurately.
  • •Fragments and encoded content. Short fragments are analyzed as-is — weak evidence yields no_signals or abstained, not a guess. Encoded payloads (base64, hex) are analyzed as literal text; decode before submitting when the payload is the content of interest.
  • •Routing rule. Treat not_applicable, no_signals, and abstained as "no actionable finding" — none of them prove the content is benign. If the case is safety-critical, route to human review or an analyzer with coverage for the declared language.

Idempotency

Send an Idempotency-Key header to make a request safe to retry. The key is scoped to your account and the canonical request identity - the request fields including detection_config when you pin one.

POST /v1/analyze
Idempotency-Key: msg_92a8...
  • •Same key + same canonical request → returns the recorded response verbatim, marked X-Sentiqra-Idempotent-Replay: true, with no additional charge. The stored usage fields describe the recorded operation's billing - the replay itself charged nothing. Records live 24 hours and hold the key, a request hash, and the response (which includes per-analyzer outcomes) - your submitted text is never persisted. If the current configuration rolls out inside the TTL and you did not pin detection_config, the replay still returns the recorded response - a replay repeats the recorded operation, not the latest defaults.
  • •Same key + different request → 409 idempotency_conflict. The canonical identity includes detection_config - pinning a different configuration under the same key conflicts rather than silently switching semantics.
  • •Partial results are preserved → a replay echoes the recorded per-analyzer outcomes, including failures. A failed analyzer is never silently retried into a success - replaying cannot expand the original outcome.
  • •Duplicate concurrent submissions → one request owns the key; concurrent duplicates get 409 idempotency_in_progress until it completes. Concurrent retries cannot create duplicate charges - usage accounting is atomic.
  • •Fresh key → a new logical operation: executes and bills normally. Use a fresh key when the previous operation's outcome is no longer the one you want.
  • •Generate a key per logical unit of work (message ID, ticket ID) - essential for queue consumers and financial/safety pipelines.

Retrying failed analyzers

After a partial result, resubmit the same key and body with X-Retry-Failed: true - an explicit child operation that extends the recorded outcome instead of replacing it.

POST /v1/analyze
Idempotency-Key: msg_92a8...
X-Retry-Failed: true
  • •Only analyzers recorded as failed or overloaded re-execute; completed runs are preserved and never re-billed. not_applicable is a permanent property of the input and is never retried.
  • •The merged response is marked retry_child: true; retried entries carry retried: true in meta.analyzers[]. usage.billed_runs is exactly what this request charged.
  • •If a retried analyzer fails again it stays failed - a further X-Retry-Failed child can retry it again. Once nothing is left to retry the endpoint returns 409 no_failed_analyzers.
  • •A failed child leaves the parent's recorded outcome intact - previously billed runs can never be re-charged.
  • •A completed analyzer that returned zero signals is a successful run - distinct from failed, billed, and never retried by a child operation.

Retries & timeouts

  • •Set a client timeout; retry only on 429, 5xx, and network errors.
  • •Use exponential backoff with jitter.
  • •Always pair retries with an idempotency key.

Rate limits & capacity

  • •Limits vary by plan; 429 responses include rate-limit headers.
  • •Throttle at the client and queue bursts rather than firing them.
  • •Size capacity in analyzer runs, not requests.
  • •Each key has a concurrent in-flight fair-share cap (default 8 analyzer runs) beneath a fleet-wide pool. 503 detector_unavailable with Retry-After means you're over it - retryable and never billed.

Observability

Record the evidence behind every call so you can debug and audit later.

  • •Log request_id plus the complete verification receipt per request - all seven fields (version, configuration, fingerprint, input_commitment, signature, key_id, binding_key_id). A partial receipt cannot be verified later.
  • •Track latency, error rate, and per-analyzer status as metrics.
  • •Record your own policy version and input-preparation version alongside the analysis evidence - see the continuity package for the full evidence list.

Measuring production quality — don't learn only from flagged cases

Reviewed alerts, customer complaints, and successful appeals are a self-selected slice of traffic, not a sample of it — the selective-labels problem. If only flagged cases receive labels, "production accuracy" describes only the flagged stratum, and "no complaint received" is not evidence of correct classification.

  • •Sample every stratum: flagged, unflagged, abstained, and failed cases can each be wrong. A representative audit draws from all four, with additional deliberate oversampling of rare or important cases where justified.
  • •Record selection probabilities: weight = 1/p lets population estimates correct for deliberate oversampling. Weighting cannot recover cases — or outcomes — that were never observable.
  • •Pending is not negative: a case awaiting its label or its delayed business outcome stays pending. It is never counted as a miss or silently dropped from the report.
  • •Blind the review: for reference audits, hide the analyzer's prediction until the reviewer has formed an initial judgment.
  • •Keep audit content yours: labeled audit samples stay in your environment — they are not shared into Sentiqra development datasets unless your commitments explicitly permit it.

The sentiqra-audit.py toolkit implements this contract: plan draws the deterministic stratified sample (with --blind review support), and report emits a quality report that discloses the sampling design, missing and pending labels, the population it can speak for — and emits full-population accuracy only when every stratum contributed labels.

Trust separation — public site vs. authenticated console

Browser storage is shared within an origin, so hosting applications with different security needs on one origin is a known risk pattern (OWASP). Today the marketing pages, console, and API gateway all live on sentiqra.tech; the isolation that protects the console is enforced in the browser surface itself:

  • •Host-only session cookie: __Host-sentiqra-session is Secure, HttpOnly, SameSite=Lax, Path=/ with no Domain attribute — the __Host- prefix means no sibling subdomain can set or overwrite it.
  • •No third-party scripts on sensitive surfaces: CSP allows scripts only from 'self' (nonce-bound) and Stripe's js.stripe.com — there are no tag managers or marketing pixels anywhere, and the allowlist is the enforcement point if one is ever proposed.
  • •No sensitive browser storage: localStorage/sessionStorage carry only theme, analytics consent, and a UX-only rate-limit note — never credentials, API keys, or analysis content. There is no service worker.
  • •No ambient cross-origin access: CORS grants no origins; API clients authenticate with Bearer tokens, not cookies, so cross-origin browser reads are denied by default.

If third-party marketing scripts or console growth justifies it, the planned split is sentiqra.tech (marketing), console.sentiqra.tech (console), api.sentiqra.tech (API) — a proposed structure, not the current deployment. A console move re-authenticates users once but never breaks Bearer API clients (no cookies involved). Subdomain separation does not remove the need for CSRF protection or server-side authorization — both stay mandatory either way.

Production checklist

☐Use separate development and production keys
☐Keep keys in secret storage
☐Pass explicit analyzer IDs
☐Set client timeouts
☐Implement controlled retries
☐Use idempotency keys
☐Handle 429 with backoff
☐Handle partial execution
☐Never treat analyzer failure as "safe"
☐Route on status + applicability + assessment, not empty signals
☐Check the analyzer\u2019s language matrix before declaring a language
☐Record request ID, configuration, fingerprint
☐Record your own policy version
☐Monitor latency and errors
☐Set usage alerts
☐Validate thresholds against labeled data
☐Audit production quality across all strata, not just flagged cases
☐Test configuration upgrades before rollout
☐Avoid logging submitted text unless required
☐Keep analytics and tag managers off authenticated screens

Sentiqra provides the signals. Your system owns the decision - and the evidence trail behind it.