Error Reference
The script is designed to never break your page — all errors are caught internally and logged to console.error. However, certain failure conditions produce observable symptoms. This page documents every error by subsystem.
The ERR_* identifiers below are documentation labels — they are not exported constants or typed error codes in the script. Errors are thrown as plain Error objects with the message strings shown. You cannot catch or match on ERR_IFRAME_TIMEOUT directly; match on the .message string instead.
General Error-Handling Pattern
// Listen for initialization failures (the only error surfaced as a DOM event)
window.addEventListener('opportify:init-failed', (event) => {
const { attempts, error } = event.detail;
// Log, fall back to an alternate form handler, or notify your monitoring system
console.error(`Opportify failed after ${attempts} attempts: ${error}`);
});
For API errors, the script logs to console.error internally. Add a console.error override in non-production environments to capture these if needed.
Initialization Errors
Errors that occur during POST /intel/v1/init or before it can be sent.
ERR_IFRAME_TIMEOUT
| Property | Value |
|---|---|
| When | The bridge iframe does not respond within ~1 second of page load |
| Message | Iframe Bridge failed to respond after 1 second. Ensure the bridge iframe is accessible and not blocked by CSP or network issues. |
| Surfaced as | opportify:init-failed DOM event after 3 retry attempts |
Root causes:
frame-srcCSP directive blockscdn.opportify.ai- Network request to CDN blocked (ad-blocker, firewall, offline)
document.bodynot yet available when the script runs (very rare edge case)
Fix:
Content-Security-Policy: frame-src 'self' https://cdn.opportify.ai;
Verify the iframe is present:
document.getElementById('opportify-bridge-iframe') // should not be null
ERR_INIT_MISSING_FIELDS
| Property | Value |
|---|---|
| When | The API returns HTTP 200 but the response body is missing clientId, sessionId, or nonce |
| Message | Init response is missing required fields |
| Surfaced as | opportify:init-failed after retries |
Root causes:
- API version mismatch
- Malformed response from a proxy or CDN layer in front of
api.opportify.ai
Fix: Check network traffic for the /intel/v1/init response body. If you use a reverse proxy, ensure it passes the response body through unmodified.
ERR_INIT_HTTP
| Property | Value |
|---|---|
| When | POST /intel/v1/init returns a non-2xx HTTP status |
| Message | Init request failed with status {status} |
| Surfaced as | console.error + opportify:init-failed after retries |
| Status | Likely cause |
|---|---|
400 | Request payload is malformed; the script automatically retries without the platform field |
401 | Invalid or expired public key |
403 | Domain not allowlisted in the Admin Console |
429 | Rate limit exceeded |
5xx | Opportify API unavailable |
Fix by status:
// 401/403 — check your key and allowlist
// Admin Console → Settings → API Keys
// Admin Console → Quick Start → Step 1 (Allowlist domain)
// 429 — reduce page load frequency or contact support
// 5xx — transient; the script retries with exponential back-off (1s, 2s, 4s)
ERR_NO_PUBLIC_KEY
| Property | Value |
|---|---|
| When | No data-opportify-key attribute found on the script tag at startup |
| Behavior | Script silently no-ops — no init request is made, no forms are instrumented |
Fix:
<script
src="https://cdn.opportify.ai/f/v0.4.3.min.js"
data-opportify-key="pk_live_YOUR_KEY"
async
></script>
Token Minting Errors
Errors from POST /intel/v1/forms/tokens (dynamic form token minting).
ERR_MINT_MISSING_SESSION
| Property | Value |
|---|---|
| When | mintFormTokens is called before init has completed (i.e. clientId, sessionId, or nonce is missing) |
| Message | clientId, sessionId, and nonce are required for token minting |
| Surfaced as | Thrown Error — caught internally, logged to console.error |
Root cause: A form was detected by MutationObserver before the init response was received. The script serializes init before token minting, so this should not occur under normal conditions.
Fix: Ensure forms are not rendered to the DOM until after DOMContentLoaded. If you must render forms immediately, use window.FormWatcher.initForm(form) after the opportify:init-failed event confirms init either succeeded or failed.
ERR_MINT_HTTP
| Property | Value |
|---|---|
| When | POST /intel/v1/forms/tokens returns a non-2xx status |
| Message | Token minting request failed with status {status} |
| Surfaced as | console.error |
Result: The affected form will have an empty opportifyToken. Submissions from that form will be flagged as untrusted by the backend.
Form Submission Errors
Errors from POST /intel/v1/submit/{endpointId}.
ERR_SUBMIT_MISSING_ENDPOINT
| Property | Value |
|---|---|
| When | submitForm is called with an empty endpointId |
| Message | endpointId is required for form submission |
| Surfaced as | Thrown Error |
Root cause: The form's action URL does not end with a recognizable endpoint ID (UUID-like last path segment).
Fix:
<!-- Correct -->
<form action="https://api.opportify.ai/intel/v1/submit/3f2504e0-4f89-11d3-9a0c-0305e82c3301">
<!-- Wrong — no endpoint ID -->
<form action="https://api.opportify.ai/intel/v1/submit/">
ERR_SUBMIT_MISSING_TOKEN
| Property | Value |
|---|---|
| When | submitForm is called with an empty opportifyToken |
| Message | opportifyToken is required for form submission |
| Surfaced as | Thrown Error |
Root cause: The opportifyToken hidden field was not injected (init didn't complete) or was manually removed from the DOM.
Fix: Ensure the script initializes before the user submits. Listen for opportify:init-failed and disable the submit button if init does not complete:
window.addEventListener('opportify:init-failed', () => {
document.querySelectorAll('button[type="submit"]').forEach((btn) => {
btn.setAttribute('title', 'Submission temporarily unavailable');
});
});
ERR_SUBMIT_HTTP
| Property | Value |
|---|---|
| When | POST /intel/v1/submit/{endpointId} returns a non-2xx status |
| Message | Form submission failed with status {status} |
| Surfaced as | Thrown Error — propagates to the submit handler |
| Status | Meaning |
|---|---|
400 | Malformed payload or missing required fields |
401 | Invalid or mismatched opportifyToken |
404 | Endpoint ID not found — check the UUID in action |
429 | Rate limit exceeded for this endpoint |
5xx | Opportify API unavailable |
Response shape on error:
{
"errorCode": "INVALID_TOKEN",
"errorMessage": "The provided opportifyToken is invalid or has expired."
}
Error handling example:
try {
const response = await fetch(
'https://api.opportify.ai/intel/v1/submit/YOUR_ENDPOINT_ID',
{ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }
);
if (!response.ok) {
const err = await response.json();
console.error(`Submission error [${err.errorCode}]: ${err.errorMessage}`);
// Show user-facing error
return;
}
const result = await response.json();
// result.accepted === true on success
if (!result.accepted) {
// Submission was received but not accepted (e.g. flagged as high-risk)
showErrorMessage('Your submission could not be processed.');
} else {
showSuccessMessage('Submitted successfully!');
}
} catch (networkError) {
console.error('Network error during form submission:', networkError);
}
Telemetry Errors
Errors from POST /intel/v1/events.
ERR_EVENTS_MISSING_SESSION
| Property | Value |
|---|---|
| When | sendEvents is called before init has completed |
| Message | clientId, sessionId, and nonce are required for telemetry |
| Surfaced as | Thrown Error — caught internally |
Root cause: Telemetry is only sent after init completes successfully. This error indicates a programming error — sendEvents was called from outside the normal script lifecycle.
ERR_EVENTS_HTTP
| Property | Value |
|---|---|
| When | POST /intel/v1/events returns a non-2xx status |
| Message | Events request failed with status {status} |
| Surfaced as | console.error; the script continues operating |
Telemetry failures are non-fatal. The script continues to instrument forms and process submissions even if telemetry delivery fails.
Crypto Errors
Errors from the Web Crypto API utilities.
ERR_CRYPTO_UNAVAILABLE
| Property | Value |
|---|---|
| When | globalThis.crypto.subtle is not available |
| Message | Web Crypto API is not available |
| Surfaced as | Thrown Error from sha256Hex / hmacSha256Hex |
Root cause: The page is served over http:// (non-secure context) in a browser that restricts Web Crypto to secure origins, or the browser is extremely old.
Fix: Serve your page over https://. Web Crypto requires a secure context.
ERR_INVALID_NONCE_HEX
| Property | Value |
|---|---|
| When | obfuscateJsonWithNonce receives a nonce that is not valid hex |
| Message | Invalid hex string |
| Surfaced as | Thrown Error |
Root cause: The nonce returned by the API was corrupted in transit or by a proxy stripping characters.
Quick Reference Table
| Error | Subsystem | Fatal? | User-visible? |
|---|---|---|---|
ERR_IFRAME_TIMEOUT | Init | No | opportify:init-failed event |
ERR_INIT_MISSING_FIELDS | Init | No | opportify:init-failed event |
ERR_INIT_HTTP | Init | No | opportify:init-failed event |
ERR_NO_PUBLIC_KEY | Init | Yes (silent no-op) | No |
ERR_MINT_MISSING_SESSION | Token Minting | No | No (console only) |
ERR_MINT_HTTP | Token Minting | No | No (console only) |
ERR_SUBMIT_MISSING_ENDPOINT | Submission | No | Form stays on page |
ERR_SUBMIT_MISSING_TOKEN | Submission | No | Form stays on page |
ERR_SUBMIT_HTTP | Submission | No | Form stays on page |
ERR_EVENTS_MISSING_SESSION | Telemetry | No | No |
ERR_EVENTS_HTTP | Telemetry | No | No |
ERR_CRYPTO_UNAVAILABLE | Crypto | Yes (init fails) | opportify:init-failed |
ERR_INVALID_NONCE_HEX | Crypto | No (events fail) | No |