OvertureDocs

Spam protection (ALTCHA)

Overture can protect a form from bots with ALTCHA, an open-source, privacy-friendly alternative to reCAPTCHA. Instead of picture puzzles, the visitor's browser solves a small proof-of-work challenge (typically in a second or two). That's trivial for one person, but expensive for a bot sending thousands of submissions. ALTCHA uses no cookies, no tracking, and no third-party service. Challenges are issued and verified by Overture itself.

Spam protection is switched on per form by the form's owner in the Overture portal. Once it is on, no submission is sent until the visitor's browser has passed the check, so bots that post straight to the API are turned away.

How it works

The check runs on a short page hosted by Overture, not inside your form. When a visitor presses submit on a protected form:

  1. The submission is validated as usual. Field errors still appear next to the fields, and the visitor stays on your page to fix them.
  2. Once the submission is valid, the visitor's browser goes to Overture's check page (https://verify.app.overtureforms.com/...). The check runs there automatically; there is nothing to click.
  3. When the check passes, Overture sends the submission and redirects the visitor to your success page, or back to the page with the form (see "Where the visitor goes next" below).

Nothing is added to your form and no ALTCHA code runs on your page.

If you use the Overture embed: nothing to do

The hosted bundle handles all of this. Keep your existing snippet:

<form data-contact-form>
  <label>Name <input type="text" name="name" required /></label>
  <label>Phone <input type="tel" name="phone" required /></label>
  <label>Email <input type="email" name="email" required /></label>
  <label>Message <textarea name="message" required></textarea></label>
  <button type="submit">Send</button>
</form>

<script src="https://script.app.overtureforms.com/contact-form.bundle.js" defer></script>
<script>
  window.addEventListener("DOMContentLoaded", function () {
    window.ContactFormHandler.initContactForms({
      publicKey: "YOUR_PUBLIC_KEY",
      formId: "YOUR_FORM_ID"
    });
  });
</script>

With every submission, the embed sends the address of the current page (and your client-side success page, if you set one) so the check page knows where to send the visitor afterwards. Forms without spam protection are unaffected.

The same applies to forms rendered with window.OvertureForms.initStandardForm(...) and to forms hosted on Overture's own pages.

Where the visitor goes next

After the check passes and the submission is sent, the check page redirects the visitor:

  • To your success page, with referenceId and submissionId added to its query string, when there is one:
    • a success page set on the page (redirectUrl in initContactForms(...) or data-contact-form-redirect-uri) when it is on the same origin as the form page, such as /thanks;
    • when you haven't set one on the page, the success page configured for the form in the portal.
  • Back to the page with the form in every other case. The embed then dispatches contact-form:success with { referenceId, submissionId } as event.detail (shortly after initContactForms(...) runs, so listeners you add right after it still hear it), and follows a client-side success page on another origin if you set one.

On the way back, the page address briefly carries overtureSubmitted, referenceId, and submissionId query parameters. The embed removes them from the address bar once it has read them.

When the visitor goes straight to a success page, contact-form:success does not fire on the form page, because the visitor has already left it. Track conversions on the success page instead.

For initStandardForm(...), coming back shows the form's landing message in place of the form and dispatches overture-form:success. A form whose landing is a redirect sends the visitor straight there.

Upgrading from the in-form check

Earlier versions put an ALTCHA widget ("I'm not a robot") inside the form. That's gone:

  • You can remove any <div data-overture-altcha> placeholder or your own <altcha-widget> from the form. The embed removes a leftover widget automatically so it can't block submission.
  • Your site no longer needs the Content Security Policy allowances the widget used (worker-src blob: and its inline styles).

Errors

Field validation failures still come back as HTTP 422 with an errors object, shown next to each field, before the visitor leaves your page (see Events & redirects).

If the check can't finish (for example, the visitor's connection drops), the check page says so and offers to try again. A check expires 30 minutes after the visitor presses submit and works only once; an expired check sends nothing, and the page asks the visitor to go back and submit the form again.

Requirements

  • JavaScript in the visitor's browser, which the embed needs anyway. The check runs on Overture's page over HTTPS.
  • Content Security Policy: nothing beyond connect-src https://submit.app.overtureforms.com, which you already need to submit.
  • Reserved field names: don't name your own fields altcha, returnUrl, or redirectUrl. They steer the spam protection, and they are never stored or emailed.

Integrating without the embed

If you post to Overture from your own code instead of the hosted bundle, add the page address and follow the check link for protected forms:

  1. Post the submission as usual, with returnUrl: the address of the page the visitor is on. For POST /submit, you may also send redirectUrl, a success page on the same origin. Send them as JSON keys or multipart form fields. For a hosted (template) form, put returnUrl at the top level of the body, next to values:

    { "values": { "name": "Jane" }, "locale": "en", "returnUrl": "https://example.com/contact" }
    
  2. Send the visitor to the check page. A valid submission to a protected form is answered with 428 Precondition Required, and nothing has been sent yet:

    {
      "message": "Spam protection check required",
      "verifyUrl": "https://verify.app.overtureforms.com/3q2-7wEXAMPLE..."
    }
    

    Navigate the visitor's browser to verifyUrl, for example window.location.assign(body.verifyUrl). Invalid submissions still get 422 with errors, and unprotected forms still get 202.

  3. Handle the return. After the check, the visitor lands on your success page (with referenceId and submissionId), or on returnUrl with overtureSubmitted (the form ID, or the hosted form's slug), referenceId, and submissionId added to its query string. Show your confirmation there.

returnUrl and redirectUrl are honored only when they are http: or https: URLs on the same origin as the page that sent the submission (its Origin header); relative URLs resolve against that origin. Anything else is ignored. Without a usable returnUrl or success page, the check page shows a thank-you message instead of redirecting.

Solving ALTCHA yourself

Integrations that already solve ALTCHA and send the proof inline keep working, and their submissions are accepted directly (202) without the check page. The protocol is the standard ALTCHA v2 format, so the official altcha widget or the altcha-lib solver work as-is:

  1. Fetch a challenge: POST https://submit.app.overtureforms.com/submit?altcha=challenge with { "formId": "YOUR_FORM_ID", "publicKey": "YOUR_PUBLIC_KEY" } (200 with a challenge when the form is protected, 204 when it isn't), or GET https://submit.app.overtureforms.com/api/forms/{tenantId}/{slug}?altcha=challenge for a hosted (template) form.
  2. Solve it, and send the base64 payload in a field named altcha (a JSON key or multipart field for POST /submit; a top-level key next to values for a hosted form).

Each challenge can be used for one submission and expires 30 minutes after it's issued. A missing payload sends the submission to the check page as above; an invalid, expired, or reused one is rejected with 422 and an altcha entry in errors.