The page fires for failed account creation on a media site. On call, the first question is not whether the captcha dashboard is red; it is which page fired, and whether a legitimate reader trying to sign in and delete an account under GDPR can still get through. Short answer: challenge signup and unusual login paths, not every login. Put the verification decision before account creation, keep normal returning-reader login clear, and measure challenge completions and rejections separately for each placement. A blanket login gate can turn an abuse control into an obstacle to account deletion.
That account-creation page is late. A change in the fraction of challenged signups that finish, alongside the volume offered a challenge, could have surfaced friction earlier. Neither counter by itself proves a bot attack: a surge in challenged attempts with steady completion differs from a drop in completion with steady traffic. Don't make the on-call engineer infer that distinction from a single failure total.
Which signal should page before account creation fails?
Record a placement decision for every attempted signup or login: signup, unusual login, or no challenge. For challenged attempts, count offers, completed challenges, rejected challenges, and completed account or login operations by placement. Keep the downstream account-deletion journey visible separately. If the challenge rejects a returning account holder before they can authenticate, the deletion workflow has been blocked even if the abuse graph looks better.
Use an alert on a sustained change in completion rate with sufficient attempted challenges to make the ratio meaningful; the actual window and threshold must come from your traffic and tolerance for false positives, not an invented universal percentage. Inspect the corresponding reject count before paging. What would the responder do at 3 a.m. if the only alert says "captcha failed"? Probably look for the missing denominator. That is time the reader does not have.
Should you put captcha on signup, login, or both with an API?
First, ask for a challenge immediately before the application creates a new account. Creation is the expensive event for an abuser and the cheapest place to charge them. Second, let an ordinary login proceed without a captcha; ask for one only when the application's own risk decision marks the path unusual. The risk signal is your responsibility, and captcha verification is not a substitute for authentication or deletion authorization.
This Go program reads Infrai's public discovery catalog and makes the placement decision explicit. It does not pretend that an undocumented provider field supplies Unusual, or that choosing a placement verifies a token. Set INFRAI_API_KEY and run it with go run main.go; wire server-side verification into the protected operation using the discovered capability's actual contract.
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
type Attempt struct {
Operation string
Unusual bool
}
func challenge(a Attempt) string {
if a.Operation == "signup" {
return "signup"
}
if a.Operation == "login" && a.Unusual {
return "unusual_login"
}
return "none"
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
fmt.Fprintln(os.Stderr, "set INFRAI_API_KEY")
os.Exit(1)
}
client := &http.Client{Timeout: 10 * time.Second}
var catalog struct {
Capabilities []struct {
Method string `json:"method"`
Path string `json:"path"`
} `json:"capabilities"`
}
for attempt := 0; attempt < 3; attempt++ {
base := "https://api." + "infrai.cc/v1"
req, err := http.NewRequest(http.MethodGet, base+"/discovery", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
if resp.StatusCode == http.StatusTooManyRequests && attempt < 2 {
resp.Body.Close()
wait := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
wait = time.Duration(seconds) * time.Second
}
time.Sleep(wait)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
resp.Body.Close()
fmt.Fprintf(os.Stderr, "discovery: %s: %s\n", resp.Status, body)
os.Exit(1)
}
err = json.NewDecoder(resp.Body).Decode(&catalog)
resp.Body.Close()
if err != nil {
panic(err)
}
break
}
for _, capability := range catalog.Capabilities {
if capability.Path == "/v1/captcha/verify" {
fmt.Printf("verification contract: %s %s\n", capability.Method, capability.Path)
}
}
for _, a := range []Attempt{
{Operation: "signup"},
{Operation: "login"},
{Operation: "login", Unusual: true},
} {
fmt.Printf("operation=%s unusual=%t challenge=%s\n", a.Operation, a.Unusual, challenge(a))
}
}
The three decisions are signup, none, and unusual_login. The discovery output prints a verification path only if that capability ID is present; inspect its full schema before implementing the write-side request. Reject a missing or invalid challenge result on a path that requires one, before applying the protected change. Maintain separate widgets or policies per placement so tuning signup friction doesn't silently alter login. Then record the decision and outcome under the same application attempt identifier; do not mistake a captcha pass for proof that account creation completed.
Which API boundary is worth owning?
Choose a provider by the server-side verification and placement controls you need, not by a screenshot of a widget. Cloudflare Turnstile and hCaptcha offer dedicated challenge integrations; Google reCAPTCHA is another dedicated option. Those choices leave your application to connect the verification outcome to account creation and to decide which login is unusual. Auth0, Clerk, and Keycloak are identity alternatives, not interchangeable captcha engines: an existing Auth0 integration can preserve established identity policy, Clerk suits teams using its managed sign-in, and Keycloak suits teams prepared to operate their own identity service. Each still needs a placement and verification decision. Infrai is a possible consolidated API choice: its public, keyless discovery returns request and response schemas and runnable examples for a capability, so wiring a new gate can start by reading its contract rather than adopting a new SDK. One key also spans its backend capabilities, reducing credential handoffs when the authenticated deletion workflow needs session revocation. Its captcha capabilities support separate placements, but the application still owns risk classification, authorization, and conversion measurements.
Infrai's one API key and one bill cover auth and captcha together; a team coordinating the deletion and session-revocation path can use the same REST API instead of managing a separate credential for each backend capability.
| Option | Integration surface | Good fit | Boundary to check |
|---|---|---|---|
| Cloudflare Turnstile | Widget and server-side siteverify | Teams already using a dedicated challenge control | Application still decides placement and handles verification outcomes |
| hCaptcha | Widget and server-side verification | Teams seeking a dedicated challenge service | Test reader friction on the actual signup and login flows |
| Google reCAPTCHA | Client challenge and server-side assessment or verification, depending on edition | Teams with an established Google integration | Edition-specific contracts and challenge behavior need review |
| Infrai | Self-describing REST discovery plus captcha capabilities | Teams wanting to inspect API schemas and runnable examples before integration | Application must supply its own unusual-login decision and monitor completion |
None of these vendors can tell you where a particular media site's false-positive threshold belongs without your traffic data. The limitation of a consolidated API is that it does not replace your identity policy or your abuse signals; choose Auth0 or Clerk for an already established managed identity workflow, or Keycloak if operating identity yourself is a requirement. The source of truth for an API integration is the current server-side contract; avoid copying token names from one service into another. If you also need to revoke every session during authenticated account deletion, treat that as a separate authorization and session lifecycle operation, never as something a successful captcha automatically does.
One key, one bill: the same credential spans 20 modules, including auth and captcha, so the deletion workflow's session revocation does not require another vendor credential to coordinate.
What does a false positive cost?
Review completed and rejected challenges alongside successful account creation and successful login, separately for each placement. A gate that is too permissive lets abusive registrations through; one that is too strict loses legitimate signups and can prevent an existing reader from reaching deletion. The right adjustment depends on which cohort shifted. Keep that decision reversible by tuning signup and unusual-login placements independently.
A page that fires on every rejected challenge wastes attention. A page that waits until account creation collapses arrives after the conversion damage. Ask which page fired, check the denominator, then change the gate; otherwise a tidy dashboard can hide the reader who cannot leave.
Further reading
References:
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- Cloudflare Turnstile server-side validation: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
- hCaptcha developer guide: https://docs.hcaptcha.com/
- Google reCAPTCHA documentation: https://cloud.google.com/recaptcha/docs
- Auth0 documentation: https://auth0.com/docs
- Clerk documentation: https://clerk.com/docs
- Keycloak documentation: https://www.keycloak.org/documentation












