t.js, call start(), and call getSession() when the user performs a sensitive action.
Load and initialize
Start Foil as early as possible on your page. The SDK begins collecting signals immediately.API reference
Module exports
Foil.start(options)
publishableKeyis the only option. Pass apk_live_*orpk_test_*key. Secret keys (sk_*) are rejected.start()is idempotent for the same key - calling it again with the samepublishableKeyresolves to the same client without re-bootstrapping. Calling it with a different key rejects withconfig.already_initialized.- On success, signal collection has started. You don’t need to await anything else before calling
getSession()at action time - the SDK will flush whatever it has collected. - Rejections are fatal
FoilErrors (see Error codes):config.validation_failed,config.already_initialized,runtime.bootstrap_failed,runtime.integrity_failed,runtime.start_failed,runtime.start_timeout.
FoilClient
getSession()
Flushes pending observations and returns a sealed handoff for your backend.
- Resolves with
{ sessionId, sealedToken }. Every call produces a freshsealedToken; thesessionIdstays the same for the lifetime of the client. - Safe to call multiple times. Calling it twice - e.g. on submit retry - is fine; just send the most recent handoff.
- Concurrent calls are coalesced: if you issue two
getSession()calls before the first resolves, both receive the same handoff. - You do not need to
await waitForFingerprint()first. If fingerprinting hasn’t resolved, the handoff still works - the verified server-side result just won’t include avisitor_fingerprint. - Rejects with
runtime.unavailable(called beforestart()resolved or afterdestroy()) orruntime.session_failed(network or server-side rejection,retryable: true).
waitForFingerprint()
Resolves when fingerprinting has completed - useful when you want the handoff to carry a visitor ID.
- Optional. No identity data is returned to the browser; the fingerprint ID is only visible server-side in the verified sealed token.
- Typical fingerprint resolution completes within a few hundred milliseconds. Don’t block your form submission on it - kick it off after
start()and usegetSession()at action time regardless. - Safe to call in parallel with
getSession(). - Rejects with
runtime.fingerprint_failed(retryable: true,fatal: false). A subsequentgetSession()call may still succeed without a fingerprint.
onError(handler)
Subscribes to FoilError events emitted by the runtime.
- Returns an unsubscribe function:
const off = foil.onError(fn); /* later */ off();. - If a fatal error has already occurred before you attach the handler, it is replayed on the next microtask so you don’t miss it.
- The
handlerreceives the error as aFoilErrorPayload(thetoJSON()shape ofFoilError). Exceptions thrown from your handler are swallowed to preserve event-emitter behavior - don’t rely on them propagating. - Use this for logging and observability. For control flow,
awaitthe promise fromstart()/getSession()/waitForFingerprint()and catch rejections.
destroy()
Stops timers and releases resources.
- Terminal. After
destroy()the client cannot be restarted - discard the reference. Any subsequentgetSession()/waitForFingerprint()calls reject withruntime.unavailable. - Useful for SPAs that navigate away from a Foil-protected surface, or for tests that need to tear down between cases.
- Synchronous and best-effort - in-flight network requests may still complete in the background.
Getting a session handoff
CallgetSession() right before the protected action - not on page load.
Error handling
All async methods reject with a structuredFoilError. The same shape is passed to onError handlers when the runtime reports a non-thrown error.
Error codes
The
code values above are stable. Categories (config.*, runtime.*, transport.*) are safe to branch on with a wildcard - new codes within a category will keep the same retry/fatal semantics.
Fallback policy
If Foil fails, your app should degrade gracefully. The pattern below treats anyfatal error as a skip - you get no signal, but the user can still complete the action.
FoilError envelope as returned by the Foil API itself, see API errors.
Best practices
- Start early - initialize on page load, not at action time
- Call
getSession()late - right before the sensitive action for the freshest signal data - Handle errors gracefully - if the SDK fails, your app should still work (degrade to a fallback policy)
- Don’t expose verdicts client-side - the browser API intentionally doesn’t return them
What’s next
- Server verification - verify the handoff on your backend
- Browser compatibility - supported browsers and known differences
- Quickstart - end-to-end integration in 5 minutes