Logotipo Hedhog

Get Hedhog updates in your inbox

New releases, fresh recipes, and breaking changes — no spam.

CAPTCHA

Hedhog can verify a CAPTCHA token server-side using Google reCAPTCHA, Cloudflare Turnstile, or ALTCHA. Today this is wired into exactly one place — the public campaign recipient list signup form — not into login, registration, or arbitrary contact forms. If you need CAPTCHA elsewhere, you can reuse the same verification logic, but you'll be wiring it up yourself rather than flipping a global switch.

Status: ⚠️ Limited scope — all three providers are genuinely implemented, but only the campaign public signup form actually calls them today.

Public signup form sending a captcha_token through validateCaptcha() to reCAPTCHA, Turnstile, or ALTCHA

Quick Setup

  1. Create an Integration Profile (type CAPTCHA, pick a provider) with its secret key at Settings → Integration Profiles
  2. Open the campaign recipient list you want to protect, enable CAPTCHA, and select the profile
  3. Embed the provider's widget on your public signup form yourself and send the solved token as captcha_token

How It Works (today)

CAPTCHA is a per-list setting on campaign_recipient_list, not a global toggle:

ColumnDescription
enable_captchaBoolean — whether this specific public signup list requires a CAPTCHA token
captcha_integration_profile_idWhich Integration Profile (type captcha) to verify against

When someone submits the public signup form (publicSignup() in CampaignRecipientListService) for a list with enable_captcha = true:

  1. The request must include a captcha_token field
  2. Hedhog loads the configured Integration Profile and looks at its provider slug
  3. It calls the matching provider's verification logic synchronously, server-side, before the recipient is ever written to the database
  4. A failed or missing token returns 400 Bad Request — the signup never happens

All three providers below are genuinely implemented and verified server-side — this isn't a "some providers are stubs" situation.

What Hedhog Does Not Do For You

There is no endpoint that returns a CAPTCHA site_key to the browser, and no endpoint that issues an ALTCHA challenge. If you're building the public-facing signup page yourself:

  • For reCAPTCHA/Turnstile, you already know your own site_key (it's the public counterpart to the secret_key you typed into the Integration Profile) — embed the provider's widget script directly with that key, you don't need Hedhog to hand it back to you
  • For ALTCHA, which needs a server to issue a signed challenge before the client can solve it, Hedhog only verifies the solved challenge — you'll need to add your own small endpoint that generates the challenge (salt + HMAC) using the same hmac_key, or use ALTCHA's own server-side helper package to do so, before validateCaptcha will have anything valid to check

Google reCAPTCHA

Console: google.com/recaptcha/admin

  1. Register a new site, choosing reCAPTCHA v2 (checkbox challenge) or v3 (invisible, score-based)
  2. Add your domain to the allowed domains list
  3. Copy the Site key (goes in your frontend widget code) and Secret key (goes in the Integration Profile)
Config fieldDescription
secret_keyServer-side secret used against https://www.google.com/recaptcha/api/siteverify

Hedhog's verification only checks success in Google's response — it does not currently enforce a v3 score threshold, so if you use v3, decide your own acceptable score and add that check if you need it.


Cloudflare Turnstile

Console: dash.cloudflare.com → Turnstile

  1. Add a new site under Turnstile, choose a widget mode (Managed, Non-interactive, or Invisible)
  2. Copy the Site key (frontend) and Secret key (Integration Profile)
Config fieldDescription
secret_keyServer-side secret used against https://challenges.cloudflare.com/turnstile/v0/siteverify

ALTCHA

ALTCHA is a self-hosted, proof-of-work CAPTCHA with no calls to a third-party verification API — verification is pure cryptography against a key only you hold.

Project: altcha.org

Config fieldDescription
hmac_keySecret used to both sign challenges (your responsibility, see above) and verify solved tokens (handled by Hedhog)

Hedhog's verification decodes the base64 token into { algorithm, challenge, number, salt, signature }, recomputes sha256(salt + number) and checks it equals challenge, then recomputes an HMAC-SHA256 over challenge with your hmac_key and checks it equals signature. Both checks must pass.


Enabling CAPTCHA on a List

  1. Create an Integration Profile of type CAPTCHA with the provider and secret_key/hmac_key above
  2. On the campaign's recipient list settings, enable CAPTCHA and select that profile
  3. Make sure your public signup form sends the solved token as captcha_token in the request body

Which Provider to Choose

  • reCAPTCHA — widest adoption, but sends visitor data to Google and the v2 challenge adds friction
  • Turnstile — privacy-friendlier, usually invisible, backed by Cloudflare's network
  • ALTCHA — zero third-party network calls and no data leaves your infrastructure, at the cost of having to host the challenge-issuing endpoint yourself

Verify It Worked

  • Submit the public signup form without solving the CAPTCHA — it should be rejected with 400 Bad Request
  • Solve the widget and submit again — the recipient should now be created in the list

Troubleshooting

SymptomLikely cause
CAPTCHA is enabled but no profile is configuredenable_captcha is on but captcha_integration_profile_id is empty
CAPTCHA validation failed for every ALTCHA submissionThe challenge wasn't signed with the same hmac_key configured in the profile, or there's no challenge-issuing endpoint at all yet
Unsupported CAPTCHA providerThe profile's provider isn't one of recaptcha, cloudflare-turnstile, altcha
Works on staging, fails in production for reCAPTCHA/TurnstileThe production domain isn't in the provider's allowed-domains list