Server Verification
Always verify tokens on your backend - never trust the client alone. A valid token proves the visitor passed a recent challenge for your site.
Verify via REST API
Send the token from your form handler to the Squeaker API. This checks signature, expiry, and one-time use (replay protection).
POST /v1/verify
Content-Type: application/json
{
"secret": "sq_secret_xxx",
"token": "eyJ...",
"remoteip": "203.0.113.1"
} { "success": true }
// or
{ "success": false, "error": "Token already used" } Common failure reasons
| Reason | Meaning |
|---|---|
Invalid or expired token | Bad signature, wrong secret, or expired |
Token already used | Replay detected |
Invalid secret key | SQUEAKER_SECRET does not match any site |
Node.js
const apiUrl = process.env.SQUEAKER_API_URL ?? 'https://api.squeaker.cc/v1';
app.post('/contact', async (req, res) => {
const token = req.body['squeaker-token'];
if (!token) return res.status(400).json({ error: 'Missing token' });
const verifyRes = await fetch(`${apiUrl}/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: process.env.SQUEAKER_SECRET,
token,
remoteip: req.ip,
}),
});
const result = await verifyRes.json();
if (!result.success) return res.status(403).json({ error: result.error });
res.json({ ok: true });
}); PHP
$token = $_POST['squeaker-token'] ?? '';
if ($token === '') {
http_response_code(400);
exit('Missing token');
}
$apiUrl = getenv('SQUEAKER_API_URL') ?: 'https://api.squeaker.cc/v1';
$payload = json_encode([
'secret' => getenv('SQUEAKER_SECRET'),
'token' => $token,
'remoteip' => $_SERVER['REMOTE_ADDR'] ?? null,
]);
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\\r\\n",
'content' => $payload,
],
]);
$response = file_get_contents("$apiUrl/verify", false, $ctx);
$result = json_decode($response, true);
if (!$result['success']) {
http_response_code(403);
exit($result['error'] ?? 'Invalid token');
} Python
import json
import os
import urllib.request
@app.route('/contact', methods=['POST'])
def contact():
token = request.form.get('squeaker-token')
if not token:
return {'error': 'Missing token'}, 400
api_url = os.environ.get('SQUEAKER_API_URL', 'https://api.squeaker.cc/v1')
payload = json.dumps({
'secret': os.environ['SQUEAKER_SECRET'],
'token': token,
'remoteip': request.remote_addr,
}).encode()
req = urllib.request.Request(
f'{api_url}/verify',
data=payload,
headers={'Content-Type': 'application/json'},
method='POST',
)
with urllib.request.urlopen(req) as resp:
result = json.loads(resp.read())
if not result.get('success'):
return {'error': result.get('error', 'Invalid token')}, 403
return {'ok': True} React / Next.js
Use the <squeaker-widget> web component directly — load squeaker.js once, no npm wrapper needed.
'use client';
import { useEffect } from 'react';
export function SignupForm() {
useEffect(() => {
const src = 'https://cdn.squeaker.cc/squeaker.js';
if (document.querySelector(`script[src="${src}"]`)) return;
const s = document.createElement('script');
s.src = src;
s.defer = true;
document.head.appendChild(s);
}, []);
return (
<form id="signup" action="/api/signup" method="POST">
<squeaker-widget
data-sitekey="sq_live_xxx"
data-api="https://api.squeaker.cc/v1"
data-mode="auto"
data-form="#signup"
data-theme="light"
/>
<button type="submit">Sign up</button>
</form>
);
} In TypeScript projects, add squeaker-widget to your JSX intrinsic elements if the compiler complains.
Security checklist
- Store
SQUEAKER_SECRETin environment variables, not source code. - Reject requests with missing or invalid tokens before any business logic.
- Call
/v1/verifyon every submission you care about (prevents token reuse). - Keep allowed domains in the dashboard up to date.
- Rotate keys from the dashboard if a secret may have leaked.