Skip to main content
Heretic returns evidence and makes no decision. Every rule below is one a customer wrote for their own application; each is shown so the fields, their limits, and the timing are exact. Substitute your own thresholds and dispositions.

The fields

One verdict read at GET /v1/verdict/{request_id} gives everything a policy is written on: Identifiers are salted per public site key and versioned by identity.version; compare only values from the same site and version. Heretic offers no lookup by identifier or address: keep the values you compare against in your own store, and read the identity and record field references for presence rules.

Example: one account per person at signup

Every signup is probed. The rule the customer wrote: a contradicted probe, or a probe that shares an address or an identifier with an existing account, is not created directly; the visitor completes a device ceremony first. A refused probe is treated the same as contradicted.

Schema

Browser

The collector runs when the page loads, so the measurement is usually finished before the form is. The submit button stays disabled until the request ID exists.
heretic() runs once per page and later calls return the same run, so awaiting it at submit time costs nothing when it already finished.

Server

challenge here mints a ceremony at POST /v1/challenge, stores the pending signup keyed by challenge_id, and returns the ceremony url as challenge_url. The page opens it with heretic.challenge, a modal frame from guard.js; the three ways to show a ceremony are on the endpoint page. When GET /v1/challenge/{id} reports passed, the signup completes with the same insert; contradicted and not_completed are the customer’s to dispose of. A variant of this rule rejects instead of challenging: replace return challenge(...) with a refusal response. A variant that counts rather than blocks: allow up to N accounts per machine_id because identical stock phones in one timezone share it, and none per instance_id.

Example: the widget, then your own duplicate check

The challenge widget already steps up contradicted and refused probes under data-escalate. What it cannot know is your account table. The customer’s rule: after siteverify, read the assessment and apply the same duplicate check.
When a ceremony ran, check.requestId names the assessment measured on the ceremony device. Its record.client_ip is that device’s address, and its identifiers are that device’s.

Handling the time the probe takes

The probe measures compute, rendering, and the connection; the request ID arrives when it finishes. The verdict is readable the moment the collector resolves. Do not add a delay before the read. Three ways customers hold the action until then: Widget tokens expire 300 seconds after issue. heretic.isExpired(id) says whether the token on the page is stale, data-expired-callback fires when it lapses, and heretic.reset(id) runs the probe again. When heretic() returns ok: false, reason says why. With sessionCreated: true the sensor opened a session that did not complete: it finalizes the session 15 seconds after it opened, and a verdict read reports refused from then on and 404 before. With sessionCreated: false no session exists and there is nothing to read. Both are the same fact for a policy: the visitor was not measured. On the server, a 503 from the verdict endpoint means Heretic cannot prove whether the assessment exists. Do not convert it into uncontradicted; hold the action as you would hold it for a missing probe.