Skip to content

Go Server SDK

Official Go server-side SDK — github.com/Captcha-La/captchala-go.

Three jobs the SDK handles for you:

  1. Validate — verify a pt_ pass token from the browser SDK.
  2. Issue — mint a one-time sct_ server token to bind the upcoming challenge to a specific action / IP / UID.
  3. Moderate — multi-modal (text + image) content moderation against the same OpenAI-compatible pipeline the dashboard uses.

Install

bash
go get github.com/Captcha-La/captchala-go

Stdlib only. No external dependencies.

Validate a token

Pass the end-user's IP (from CF-Connecting-IP / X-Forwarded-For, falling back to RemoteAddr). Optional, but recommended — it's used for additional risk checks.

go
import captchala "github.com/Captcha-La/captchala-go"

client := captchala.NewClient(appKey, appSecret)

func handler(w http.ResponseWriter, r *http.Request) {
    token := r.FormValue("captcha_token")

    result, err := client.ValidateWithClientIP(token, false, clientIP(r))
    if err != nil || !result.Valid {
        http.Error(w, result.Error, http.StatusBadRequest)
        return
    }
    // Verification passed; proceed with the request.
    // result.CaptchaArgs has Platform / UserIP / Referer / Pkg / SolvedAt / RiskScore
}

// Real end-user IP behind a CDN / proxy
func clientIP(r *http.Request) string {
    if ip := r.Header.Get("CF-Connecting-IP"); ip != "" {
        return ip
    }
    if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
        return strings.TrimSpace(strings.Split(xff, ",")[0])
    }
    host, _, _ := net.SplitHostPort(r.RemoteAddr)
    return host
}

No IP handy? Use client.Validate(token) — verification still works, just without the extra IP-based risk signal.

If the original server_token was issued with bind_uid, compare:

go
if result.UID != expectedUserID {
    http.Error(w, "user mismatch", http.StatusBadRequest)
    return
}

The solve-time context is also returned in result.CaptchaArgs for reference:

go
ip := result.CaptchaArgs.UserIP   // visitor IP recorded at solve time

Issue a server token

Recommended for high-value flows (login, register, payment): backend mints a one-time sct_ token, hands it to the browser, browser uses it as the serverToken prop. Single-use, action-scoped, optionally IP/UID-bound.

go
issue, err := client.IssueServerTokenWithOptions("login", captchala.IssueOptions{
    BindingIP: userIP,    // backend rejects token if a different IP redeems it
    TTL:       300,       // seconds
    MaxUses:   5,         // SDK retry budget
    BindUID:   userID,    // pair with ValidateResult.UID on verify
})
if err != nil || !issue.OK {
    http.Error(w, issue.Error, http.StatusBadRequest)
    return
}
// hand issue.Token to the browser

Defaults-only shortcut:

go
issue, err := client.IssueServerToken("login")

Moderate content

Multi-modal — accepts a slice of ModerationItem (text and/or image_url in OpenAI-compatible format):

go
result, err := client.ModerationCheck([]captchala.ModerationItem{
    captchala.TextItem(userComment),
    captchala.ImageURLItem(uploadedImageURL),
}, userID)

if err != nil || !result.OK {
    http.Error(w, result.Error, http.StatusBadRequest)
    return
}

if result.Flagged {
    if result.HasCategory("violence", "csam") {
        // hard block
    }
}

Plain-text shortcut:

go
result, err := client.ModerationText("user comment here", userID)

Categories are model-defined; iterate result.Categories defensively rather than hard-coding a fixed set.

Types

TypeFields
ValidateResultValid, Error, UID, ChallengeID, Action, Offline, ClientOnly, Warning, CaptchaArgs
CaptchaArgsPlatform, UserIP, Referer, Pkg, SolvedAt, RiskScore (informational)
IssueResultOK, Token, ExpiresIn, IssuedAt, Error, Message
IssueOptionsBindingIP, TTL, MaxUses, BindUID
ModerationItemType, Text, ImageURL (use TextItem() / ImageURLItem() helpers)
ModerationResultOK, Flagged, Categories, ContentType, Raw, Error, Message; method HasCategory(...names)

MIT-licensed examples · CaptchaLa is operated independently