Server Verification
How to securely verify NeoCaptcha tokens on your backend.
Updated 2/22/2026
Server Verification
Server verification is the core security boundary in NeoCaptcha integrations.
Rule: Never trust frontend success alone. Always verify the received token on your backend before allowing sensitive actions.
API reference
POST https://neocaptcha.com/api/v1/verify
Accepts application/json or application/x-www-form-urlencoded.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
solution | string | Yes | The verification token from the widget |
secret | string | Yes | Your site secret |
siteKey | string | Yes | Your site key |
expectedOrigin | string | No | Origin to validate against (e.g. https://app.example.com) |
Response
| Field | Type | Description |
|---|---|---|
success | boolean | true if the token is valid |
verifiedAt | string | ISO timestamp of verification |
End-to-end verification flow
- User completes widget challenge in browser.
- Widget emits a
verificationToken. - Frontend sends token to your backend route.
- Your backend calls
POST /api/v1/verify. - NeoCaptcha returns verification result with metadata.
- Your backend enforces policy and either accepts or rejects the request.
Minimal Node/TypeScript verification helper
export type VerifyCaptchaInput = {
response: string;
expectedOrigin?: string;
};
export async function verifyCaptcha(input: VerifyCaptchaInput) {
const res = await fetch('https://neocaptcha.com/api/v1/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
solution: input.response,
secret: process.env.NEOCAPTCHA_SITE_SECRET,
siteKey: process.env.NEOCAPTCHA_SITE_KEY,
expectedOrigin: input.expectedOrigin,
}),
});
if (!res.ok) {
throw new Error(`NeoCaptcha verify error: ${res.status}`);
}
return res.json();
}Example route policy
app.post('/api/signup', async (req, res) => {
const result = await verifyCaptcha({
response: req.body.captchaToken,
expectedOrigin: 'https://app.example.com',
});
if (!result.success) {
return res.status(400).json({ error: 'captcha_invalid' });
}
// Continue with user creation
return res.status(200).json({ ok: true });
});Replay protection and token lifecycle
NeoCaptcha verification tokens are one-time credentials — each token can only be verified once.
Implications for integrators:
- Do not cache tokens for reuse.
- Do not allow "retry submit" with stale form payloads containing old token values.
- If your action fails after captcha validation due to unrelated errors, ask the user to re-run the captcha before the next sensitive attempt.
Error handling guidance
Treat verification errors differently by category:
- Invalid token: return user-facing "please retry captcha."
- Expired token: prompt widget refresh and challenge re-run.
- Origin mismatch: investigate integration or abuse attempts.
- Verification API unavailable: fail closed for high-risk routes, fail open only for low-risk and with logging.
Language examples
Python (FastAPI)
import httpx
from fastapi import HTTPException
async def verify_captcha(token: str):
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.post(
'https://neocaptcha.com/api/v1/verify',
json={
'solution': token,
'secret': NEOCAPTCHA_SITE_SECRET,
'siteKey': NEOCAPTCHA_SITE_KEY,
'expectedOrigin': 'https://app.example.com',
},
)
data = response.json()
if not data.get('success'):
raise HTTPException(status_code=400, detail='Captcha verification failed')
return dataGo
payload := map[string]string{
"solution": token,
"secret": os.Getenv("NEOCAPTCHA_SITE_SECRET"),
"siteKey": os.Getenv("NEOCAPTCHA_SITE_KEY"),
"expectedOrigin": "https://app.example.com",
}
b, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://neocaptcha.com/api/v1/verify", bytes.NewBuffer(b))
req.Header.Set("Content-Type", "application/json")Security checklist
- Site secret stored in backend secret manager, never exposed to the browser.
- Every sensitive route verifies captcha before side effects.
-
expectedOriginenforced. - Replay tokens rejected by design and in your flow logic.
- Observability added for failure rates and abuse spikes.