Skip to main content
Foil’s browser surface is intentionally small. Import 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)

Bootstraps the runtime and begins signal collection. Safe to call on page load.
  • publishableKey is the only option. Pass a pk_live_* or pk_test_* key. Secret keys (sk_*) are rejected.
  • start() is idempotent for the same key - calling it again with the same publishableKey resolves to the same client without re-bootstrapping. Calling it with a different key rejects with config.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 fresh sealedToken; the sessionId stays 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 a visitor_fingerprint.
  • Rejects with runtime.unavailable (called before start() resolved or after destroy()) or runtime.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 use getSession() at action time regardless.
  • Safe to call in parallel with getSession().
  • Rejects with runtime.fingerprint_failed (retryable: true, fatal: false). A subsequent getSession() 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 handler receives the error as a FoilErrorPayload (the toJSON() shape of FoilError). 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, await the promise from start() / getSession() / waitForFingerprint() and catch rejections.

destroy()

Stops timers and releases resources.
  • Terminal. After destroy() the client cannot be restarted - discard the reference. Any subsequent getSession() / waitForFingerprint() calls reject with runtime.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

Call getSession() right before the protected action - not on page load.
The handoff contains:
Every call produces a fresh handoff. The browser never receives verdicts, scores, or visitor IDs.

Error handling

All async methods reject with a structured FoilError. 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 any fatal error as a skip - you get no signal, but the user can still complete the action.
For server-side error handling and the rest of the 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