Documentation menuBrowser SDK

Browser SDK

Reference for the BlockAlt browser script: creating a client, getting tokens, preparing early, cancelling, progress, forms and errors.

On this page

Load the script #

HTML
<script src="https://blockalt.click/v1/assets/blockalt.c8b2d9539dbbc932999adb0f51a36bddd4e99899f543e1505818841878cf9813.js"
  integrity="sha384-pblByFDqcJxRfhDh18GH3gRsWy9ZaS4AjDc6OpozzQqvkuYvyin+4BWcKXxA+bN8"
  crossorigin="anonymous"></script>

The script defines one global, BlockAlt. It sets no cookies, uses no browser storage and talks only to apiUrl, without credentials or referrer: one request for the signed configuration and its challenge, one to exchange the solved challenge for a token, with no CORS preflight. See installation for the integrity hash and keys.

Forms without code #

Configure the client on the script tag and mark each protected form with its action:

HTML
<script src="https://blockalt.click/v1/assets/blockalt.c8b2d9539dbbc932999adb0f51a36bddd4e99899f543e1505818841878cf9813.js"
  integrity="sha384-pblByFDqcJxRfhDh18GH3gRsWy9ZaS4AjDc6OpozzQqvkuYvyin+4BWcKXxA+bN8" crossorigin="anonymous"
  data-site-key="ba_pk_your_public_site_key"
  data-api-url="https://blockalt.click"
  data-config-public-keys="MCowBQYDK2VwAyEAXmYdcBh/jf+z+0H+hs2QvZ9UG4PBgkRvHX/JuXyjIL8= MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE34qdEtOeD21+CmmfTmFyRr/vySsY1TD6BJZFtwF37pkelvN2vRYWWHdLrUouzkQXEfnA7V50xK2QcmnBCGsxbA== MCowBQYDK2VwAyEAtkfghYcdvSRf2wA01gjQtgTqdYGjaDUZsOPXzgLnnT0= MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYYmQNAMnVKK7G3VVguAk3SC/RFT5SDjCoxcFMOPFx0vmsGycJWOK8hCdxuTCHlvShng1fo0y4Q9PJHPIGQ9EfA=="></script>

<form method="post" action="/signup" data-blockalt-action="signup">
  <input name="email" type="email" required>
  <button>Create account</button>
</form>
  • The work starts when the form first receives focus, so the click rarely waits.
  • Just before the form submits, the token is written into a hidden blockalt-token input (created if missing). Rename it with data-input-name on the script tag or data-blockalt-input on the form.
  • While the token is being obtained the form carries aria-busy="true", and a second submit is ignored.
  • If no token can be obtained, the form receives a cancelable, bubbling blockalt:error event whose detail is a BlockAltError. Cancel it to keep the visitor on the page and show a message; otherwise the form submits with an empty token and your backend refuses it.
  • data-api-url defaults to the script's own origin; data-config-public-keys takes the keys separated by spaces or commas. The configured client is available as BlockAlt.client.
JavaScript
document.addEventListener("blockalt:error", (event) => {
  event.preventDefault(); // stay on the page
  event.target.querySelector(".error").textContent = "Please try again in a moment.";
});

BlockAlt.create(options) #

JavaScript
const protection = BlockAlt.create({
  siteKey: "ba_pk_your_public_site_key",
  apiUrl: "https://blockalt.click",
  // Every key of GET /v1/keys (configPublicKeys), standby keys included.
  configPublicKeys: ["MCowBQYDK2VwAyEAXmYdcBh/jf+z+0H+hs2QvZ9UG4PBgkRvHX/JuXyjIL8=","MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE34qdEtOeD21+CmmfTmFyRr/vySsY1TD6BJZFtwF37pkelvN2vRYWWHdLrUouzkQXEfnA7V50xK2QcmnBCGsxbA==","MCowBQYDK2VwAyEAtkfghYcdvSRf2wA01gjQtgTqdYGjaDUZsOPXzgLnnT0=","MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYYmQNAMnVKK7G3VVguAk3SC/RFT5SDjCoxcFMOPFx0vmsGycJWOK8hCdxuTCHlvShng1fo0y4Q9PJHPIGQ9EfA=="],
});

Returns a client for one site. Create it once per page and reuse it.

OptionTypeMeaning
siteKeystringPublic site key, ba_pk_…
apiUrlstringhttps://blockalt.click; HTTPS required, plain HTTP only for localhost
configPublicKeysstring[]Every key listed by GET /v1/keys, standby keys included (a single configPublicKey is still accepted)
timeoutMsnumberDeadline for each request to the API; default 5,000 ms
workTimeoutMsnumberDeadline for the proof of work; default 60,000 ms, never beyond the challenge's lifetime

execute(action, options) #

JavaScript
const token = await protection.execute("signup", {
  signal: controller.signal, // optional AbortSignal
  onProgress: ({ progress }) => (meter.value = progress), // optional, 0 to 1
});

Fetches the signed configuration and a challenge, solves the proof of work and resolves with a single-use token for action. Send it to your backend with the request it protects and verify it there.

  • Call it when the visitor submits, not when the page loads: a token expires after 120 seconds and works once. Each attempt needs a new token.
  • It resolves with an empty string when the site is paused or unpaid. Forward it unchanged; siteverify answers success: true with the reason paused or unpaid. An empty answer prepared earlier is never reused blindly: execute asks for the configuration again, so a site resumed or topped up meanwhile gets a real token. Only when BlockAlt cannot be reached at that moment is the empty answer of the last 30 seconds used.
  • signal cancels the work and the pending requests; the promise then rejects with the code aborted.
  • onProgress receives { progress, solved, puzzles }, with progress from 0 to 1, so slow devices can show progress.
  • A refusal for a changed configuration or an expired or reused challenge is retried once with a fresh challenge, automatically.

prepare(action, options) #

JavaScript
form.addEventListener("focusin", () => protection.prepare("signup").catch(() => {}), { once: true });

Obtains a token ahead of time, so the next execute(action) returns at once. One token per action is kept until 20 seconds before it expires and is handed out only once; a failed preparation is simply redone by execute. An empty answer (paused or unpaid site) is asked again by prepare after 5 seconds and by every execute. The returned promise settles when the preparation finishes.

Sending a token with fetch #

JavaScript
async function signup(data) {
  const token = await protection.execute("signup");
  const response = await fetch("/api/signup", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ ...data, token }),
  });
  if (!response.ok) throw new Error("Sign-up failed");
}

A header such as BlockAlt-Token works too; read it on your server instead of the body field.

Errors #

execute and prepare reject with a BlockAltError (also available as BlockAlt.BlockAltError). Check error.code; error.reason carries the server's reason when it gave one, and error.retryAfter the seconds to wait after a rate limit (from the Retry-After header).

JavaScript
try {
  token = await protection.execute("login");
} catch (error) {
  if (error instanceof BlockAlt.BlockAltError && ["network", "timeout", "rate_limited"].includes(error.code))
    showRetry(error.retryAfter);
  else showMessage("We could not verify this browser.");
  return;
}
CodeMeaning and what to do
unsupported_browserNo secure context, WebCrypto or MessageChannel (see browser support). Offer another way to complete the action
networkOffline, DNS or TLS failure, or a 5xx answer. Let the visitor retry
timeoutA request (5 s by default) or the work (60 s by default) took too long. Let the visitor retry
rate_limitedToo many requests from this visitor's network; wait retryAfter seconds when given
refusedBlockAlt refused issuance; reason says why, for example origin_denied, domain_unverified or rule_blocked
abortedYour signal cancelled the call
invalid_configBad or unpinned configuration signature, an action the site does not have, or a malformed answer. Check your keys and actions
clockThe server still found the challenge expired after a fresh retry

Log error.code and error.reason (never the token) while you debug an installation: issuance refusals are counted per site, minute and reason, not logged one by one, so the page's own error is the quickest way to see why a browser was refused.

BlockAlt.supported() #

JavaScript
if (!BlockAlt.supported()) showAlternative();

Returns true when the browser has what the script needs: a secure context, WebCrypto, fetch and MessageChannel. It makes no network request. When it returns false, execute rejects with unsupported_browser.

BlockAlt.createReader(options) #

Creates a helper for protected same-origin GET requests and event streams. See the read guard guide.

Something unclear or missing on this page? Write to support@blockalt.click.