Errors

Every error body is JSON. This page lists each status you can see, where you see it, and what to do.

In the browser

The collector reports errors by throwing or by rejecting the promise from ready(), settled() or verify():

Error Cause What to do
octet: `challenge` is required. … start() or verify() got no challenge. Get a challenge on your backend and pass it in. See Get a challenge.
octet: this challenge was already used. … The collector already sent this challenge. ready() reruns the collection 90 seconds after the last one, or after a failed POST, and a second verify() with the same config does the same. Get a new challenge from your backend and call start() or verify() with it.
octet: invalid apiUrl "…" apiUrl is not a URL. Set apiUrl to your edge's origin.
octet: apiUrl must be https://… apiUrl is not https://. Use https://. http:// works only for localhost, 127.0.0.1 and [::1].
octet: the collector needs a secure context (https) for crypto.subtle The page isn't served over HTTPS, so the browser offers no WebCrypto, which the collector needs to encrypt the collection. Serve the page over HTTPS. http:// works only for localhost, 127.0.0.1 and [::1].
octet: call start() before ready() ready() ran before start(). Call start() at page load.
octet: call start() before settled() settled() ran before start(). Call start() at page load.
octet: unknown mode "…" mode is not 'full', 'lite' or 'passive'. Fix the value, or leave mode out to get full.
signal report failed: <status> Your edge answered with an error status. Look up the status under From your edge.
TypeError from fetch The browser could not complete the POST. Usually CORS, DNS, TLS, or the edge is down. Check the browser console, ALLOWED_ORIGIN, and the edge's /health.
AbortError, or your own abort reason Your code aborted the collection. None. If the abort came before the POST reached your edge, no verdict was produced.

Blocked or failed measurements never cause an error. If the WebSocket to your edge fails, the collector sends what it has and results are weaker. The usual causes are a Content Security Policy without wss:// for your edge, and networks that block WebSockets.

From your edge

These are the responses to POST /v1/signals, which the browser sees:

Status Body Cause What to do
200 {"ok":true} Octet accepted the session. None.
400 {"error":"read_failed"} The edge could not read the request body. Retry.
405 {"error":"method_not_allowed"} A method other than POST or OPTIONS. Use POST.
502 {"error":"octet_unreachable"} The edge could not reach Octet. Check OCTET_URL, outbound TCP 443, and DNS on the edge host. If you set OCTET_CA_FILE, check that Octet's server certificate chains to it. The edge's log has the detail.
503 {"error":"edge_signing_failed"} From v1.7.0. The edge couldn't sign the session, for example because its cloud key service was unreachable. It sent nothing to Octet. Check the key service and the edge's access to it. The edge's log has the detail. See Where to keep the signing key.
Octet's status {"error":"octet_rejected","reason":"<reason>","status":<status>} Octet refused the session. The edge passes Octet's status and reason code through. Look up the reason below.

When Octet refuses a session, the edge logs one line with the status and reason, with no end-user data. reason is unknown when Octet's reply carries no plain reason code.

Reasons Octet gives your edge

Status Reason Cause What to do
401 missing_license LICENSE is not set on the edge. Set LICENSE and restart.
401 bad_vendor_prefix, malformed_token, bad_footer, bad_payload, bad_signature LICENSE is not a valid license token. It may be truncated, or be a read token. Copy the license token again from Octet's email.
401 wrong_typ, wrong_issuer, product_not_licensed, platform_not_licensed The token is valid but not an Octet Browser license. Use the license token issued for Octet Browser.
401 expired, not_yet_valid, missing_exp, missing_iat_and_nbf The license token is outside its validity period, or the edge host's clock is wrong. Check the host's clock. If it is right, ask Octet for a new license token.
401 revoked Octet revoked the license token. Deploy the replacement Octet sent you, or contact Octet.
401 unknown, or any reason not listed on this page Octet could not check the license. Retry. If it persists, contact developer@octetproof.com.
401 client_cert_required The edge presented no client certificate, or one Octet didn't issue. Check EDGE_CLIENT_CERT_FILE and EDGE_CLIENT_KEY_FILE, and that the certificate hasn't expired. See Deploy the Edge.
403 client_cert_licence_mismatch The client certificate was issued for a different license than the one in LICENSE. Use a certificate issued for this license, from its Edge configuration tab in the Octet portal, under Certificates.
403 client_cert_revoked The client certificate was revoked in the Octet portal. Issue a new certificate in the Edge configuration tab, under Certificates, install it and restart the edge. See Credentials.
401 edge_signature_required Octet requires a signature from your edge, and the session had none, because EDGE_SIGNING_KEY is unset. Set EDGE_SIGNING_KEY, restart the edge and register its key. See Deploy the Edge.
401 edge_key_unknown Octet requires a signature from your edge, and the key that signed the session isn't registered for this license, or was revoked. Register the kid and public key from the edge's startup log in the Edge configuration tab, under Signing keys. See Credentials.
401 edge_signature_stale The signature's time is out of range, usually because the edge host's clock is wrong. Check the edge host's clock, for example that NTP is running.
401 edge_signature_mismatch The session Octet received differs from the one your edge signed. Something between your edge and Octet changed the request. Make sure nothing between the edge and Octet, such as an outbound proxy, rewrites the request.
401 edge_signature_invalid The edge's signature doesn't verify, or its key is registered for a different license. Update the edge to the latest release and restart it. If it persists, contact developer@octetproof.com.
401 challenge_required The collection carries no challenge. A collector older than v1.5.0 gets this. Update the collector, and pass it a challenge from your backend. See Get a challenge.
401 challenge_invalid The challenge is not one Octet issued, or it was changed. Pass the challenge from POST /v1/challenge to the page unchanged.
401 challenge_expired The challenge is more than 10 minutes old. Get a new challenge and call start() again. Get the challenge when you render the page, not earlier.
401 challenge_mismatch The challenge was issued for a different sessionRef, or under a different license from the edge's. Request the challenge with the same sessionRef you pass to start(), with a read token from the same license as the edge's LICENSE.
409 challenge_spent Octet already accepted a collection with this challenge. Get a new challenge for each collection.
400 invalid_json The body is not JSON. Send the collector's POST unchanged.
400 unsealed_bundle The body isn't a sealed collection. A collector older than v1.3.0 gets this, and so does an empty {} body. Update the collector.
400 unknown_seal_key The collector is sealed to a key Octet no longer accepts, because the collector is too old. Update the collector. See Release Notes.
400 bad_seal, bad_signature, bad_payload The sealed collection doesn't open, or its contents don't check out. This usually means a proxy or a script on the page changed the body. Send the collector's POST unchanged.
400 missing_fields The sealed collection opened, but isn't a complete collection. Send the collector's POST unchanged.
409 replayed_bundle Octet accepted this collection recently, through this edge or another. A copy of a collection is refused. None for the original session, whose verdict stands.
413 payload_too_large The body is larger than Octet accepts. Send the collector's POST unchanged.

A 401 with bad_signature or bad_payload concerns the license token, and a 400 with the same reason concerns the collection.

From POST /v1/challenge

These are the responses to POST /v1/challenge, which your backend sees. If you send the request through your edge, the edge returns Octet's response unchanged.

Status Body Cause What to do
200 {"challenge":"…","exp":…} Octet issued a challenge. Pass challenge to the page.
400 {"error":"invalid_json"} The body is not JSON. Send {"sessionRef":"…"} with content-type: application/json.
400 {"error":"invalid_session_ref"} sessionRef is missing, not a string, empty, or longer than 256 characters. Send the sessionRef you created for the page view.
400 {"error":"invalid_client_ip"} clientIp is present but isn't an IPv4 or IPv6 address. Send the user's IP address as a string, or leave clientIp out.
401 The same bodies as for GET /v1/verdict The read token was refused. See From the Octet API.
413 {"error":"payload_too_large"} The body is larger than 4 KB. Send only sessionRef and clientIp.
429 {"error":"rate_limited"} Your license requested too many challenges in one minute. The limit is far above any real page-view rate. Retry after a short wait. If it persists, check that your backend requests one challenge per page view, and that your read token hasn't leaked.

Through your edge, you can also get the edge's own errors: 405 with method_not_allowed for a method other than POST, 400 with read_failed, and 502 with octet_unreachable. See From your edge.

From the Octet API

These are the responses to GET /v1/verdict/{sessionRef}, which your backend sees:

Status Body Cause What to do
200 The verdict See Verdict Reference. Apply your policy.
400 {"error":"stage_not_allowed"} You sent stage, and your license doesn't have the early verdict. Leave out stage, or ask Octet for the early verdict.
400 {"error":"invalid_stage"} stage is not provisional or final. Send provisional or final.
404 {"status":"pending","ref":"…"} No verdict for this sessionRef yet (with the early verdict, no final verdict yet unless you asked for stage=provisional), the verdict is more than 2 minutes old, the sessionRef is wrong, or the session arrived under another license. Retry with waitMs. If it persists, check that the page passes the same sessionRef to start().
401 {"error":"unauthorized"} No Authorization: Bearer octet_read_… header. A license token sent as the bearer also gets this. Send your read token.
401 {"error":"expired"} The read token has expired. Create a new one. See Credentials.
401 {"error":"revoked"} The read token, or its license, has been revoked. Create a new one, or contact Octet if the license was revoked.
401 {"error":"not_yet_valid"} The read token isn't valid yet. Check your server's clock.
401 malformed_token, bad_footer, bad_payload, bad_signature, unknown_kid:… The read token is damaged or incomplete. Copy it again, or create a new one.
401 wrong_typ, wrong_issuer, wrong_audience, missing_scope, wrong_env, missing_lid, missing_exp, missing_iat_and_nbf, unsupported_schema:… The token is not an Octet Browser read token. Create a read token under Read tokens in the Verdict API tab of your Octet Browser license.
401 Any reason not listed on this page Octet could not check the read token. Retry. If it persists, contact developer@octetproof.com.
503 {"error":"verdict_signing_unavailable"} Octet could not sign the verdict. Retry after a short wait. Octet holds the verdict for 2 minutes.

The 401 reason codes from GET /v1/verdict appear exactly as listed, and some carry a suffix after a colon.

From GET /v1/device/{deviceKey}

These are the responses to GET /v1/device/{deviceKey}, which returns one browser's device record:

Status Body Cause What to do
200 The record Your license has a record under this deviceKey. See below. Pass it on to your user, for example to answer an access request.
400 {"error":"invalid_device_key"} The path doesn't hold a deviceKey in the form <prefix>_<value>. Send the deviceKey exactly as the verdict carried it.
401 The same bodies as for GET /v1/verdict The read token was refused. See From the Octet API.
404 {"error":"device_record_not_found"} Your license has no record under this deviceKey, for example because it was deleted. A deviceKey from another license gets the same answer. Nothing. There is no record for this browser on your license.
404 {"error":"device_records_not_configured"} This Octet server keeps no device records. Until device records start for your license (from 1 December 2026), every request gets this. Nothing. There is no record to return.

The response carries Cache-Control: no-store. A 200 body has these fields. Times are ISO 8601 in UTC, and a value Octet doesn't have is null.

Field Description
lid Your license identifier.
deviceKey The deviceKey the record is held under.
firstSeen, lastSeen When the browser was first and last seen.
privacyProtection The privacy protection recognised in the browser, for example brave, or null.
maxProcessorCount The highest processor count the browser reported, or null.
canvasHashes, audioHashes Up to 20 each, most often seen first. Each has hash, count and lastSeen. hash is keyed for your license: it matches nothing outside your license's records.
sessions Up to 200, oldest first. Each has time, verdict (country, confidence, alarm), ipCountry (the country of the user's IP address, never the address), checks (one true or false for each of Octet's server-side checks) and networkTag (the keyed tag of the user's network).

Field names don't change. Later versions may add fields, so ignore fields you don't recognise.

From DELETE /v1/device/{deviceKey}

These are the responses to DELETE /v1/device/{deviceKey}, which deletes one browser's device record:

Status Body Cause What to do
200 {"deleted":true} Octet deleted the record. Nothing.
200 {"deleted":false} Your license has no record under this deviceKey, for example because it was already deleted. Nothing.
400 {"error":"invalid_device_key"} The path doesn't hold a deviceKey in the form <prefix>_<value>. Send the deviceKey exactly as the verdict carried it.
401 The same bodies as for GET /v1/verdict The read token was refused. See From the Octet API.
404 {"error":"device_records_not_configured"} This Octet server keeps no device records. Until device records start for your license (from 1 December 2026), every request gets this. There is no record to delete.

Key sets

Each of these returns 200 with a JSON document:

URL on https://geo.octetproof.com What it is
/.well-known/browser-verdict-jwks.json The verdict key set. Cache it for up to 5 minutes. See Verify the Signed Token.
/.well-known/browser-verdict-jwks-archive.json Every key Octet has signed verdicts with. See Keep Verdicts for Audit.
/.well-known/browser-challenge-jwks.json The challenge key set. See The chain.
/.well-known/octet-browser-manifest.json.jws The manifest signed by Octet's root keys. See Check a key against Octet's root keys.
TermsPrivacy© 2026 Understone, Inc. d/b/a Octet