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:
- The submission is validated as usual. Field errors still appear next to the fields, and the visitor stays on your page to fix them.
- 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. - 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
referenceIdandsubmissionIdadded to its query string, when there is one:- a success page set on the page (
redirectUrlininitContactForms(...)ordata-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.
- a success page set on the page (
- Back to the page with the form in every other case. The embed then
dispatches
contact-form:successwith{ referenceId, submissionId }asevent.detail(shortly afterinitContactForms(...)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, orredirectUrl. 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:
Post the submission as usual, with
returnUrl: the address of the page the visitor is on. ForPOST /submit, you may also sendredirectUrl, a success page on the same origin. Send them as JSON keys or multipart form fields. For a hosted (template) form, putreturnUrlat the top level of the body, next tovalues:{ "values": { "name": "Jane" }, "locale": "en", "returnUrl": "https://example.com/contact" }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 examplewindow.location.assign(body.verifyUrl). Invalid submissions still get422witherrors, and unprotected forms still get202.Handle the return. After the check, the visitor lands on your success page (with
referenceIdandsubmissionId), or onreturnUrlwithovertureSubmitted(the form ID, or the hosted form's slug),referenceId, andsubmissionIdadded 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:
- Fetch a challenge:
POST https://submit.app.overtureforms.com/submit?altcha=challengewith{ "formId": "YOUR_FORM_ID", "publicKey": "YOUR_PUBLIC_KEY" }(200with a challenge when the form is protected,204when it isn't), orGET https://submit.app.overtureforms.com/api/forms/{tenantId}/{slug}?altcha=challengefor a hosted (template) form. - Solve it, and send the base64 payload in a field named
altcha(a JSON key or multipart field forPOST /submit; a top-level key next tovaluesfor 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.