Authentication & Signing
Every outbound request from betterfound is signed with HMAC-SHA256 using a per-tenant secret. Your edge can verify the signature to confirm the request genuinely came from betterfound and has not been tampered with.
The four standard headers
Every betterfound request carries these headers regardless of integration tier:
User-Agent: shopmind-fetch/1.0 (+https://shopmind.io/agent; tenant=<tenantId>)
X-Shopmind-Tenant: <tenantId>
X-Shopmind-Timestamp: <unix-ms>
X-Shopmind-Signature: <hex HMAC-SHA256>Signature computation
The signature is an HMAC-SHA256 over a canonical string:
HMAC-SHA256(
secret,
<tenantId> + "\n" + <timestamp> + "\n" + <originalUrl>
)Where:
secret— your per-tenant 32-byte hex secret (generated at onboarding, shown once in the dashboard under Settings → Fetch Agent)timestamp— Unix timestamp in milliseconds (X-Shopmind-Timestamp)originalUrl— the absolute URL being fetched (for Tier B, this is the original brand URL, not your proxy endpoint URL)
Replay protection
Validate that X-Shopmind-Timestamp is within ±5 minutes of your edge clock. betterfound does not reuse signatures across timestamps — each request has a unique timestamp.
If your edge clock drifts more than 5 minutes from UTC, legitimate betterfound requests will fail signature validation. Ensure NTP sync is running on your edge nodes.
Reference verification — Node.js
import { createHmac, timingSafeEqual } from 'node:crypto'
const SHARED_SECRET = process.env.BETTERFOUND_FETCH_AGENT_SECRET // hex
const REPLAY_WINDOW_MS = 5 * 60 * 1000
export function verifyBetterfoundRequest(req, originalUrl) {
const tenantId = req.headers['x-shopmind-tenant']
const timestamp = Number(req.headers['x-shopmind-timestamp'])
const provided = String(req.headers['x-shopmind-signature'] ?? '')
if (!tenantId || !Number.isFinite(timestamp) || !provided) return false
if (Math.abs(Date.now() - timestamp) > REPLAY_WINDOW_MS) return false
const expected = createHmac('sha256', SHARED_SECRET)
.update(`${tenantId}\n${timestamp}\n${originalUrl}`)
.digest('hex')
return provided.length === expected.length &&
timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(provided, 'hex'))
}Always use
timingSafeEqual for the comparison — a naive string equality check is vulnerable to timing attacks.Where to find your secret
Your per-tenant fetch agent secret is generated at onboarding and shown once in the betterfound dashboard:
- Go to Dashboard → Settings → Fetch Agent
- Click Reveal secret (requires MFA confirmation)
- Copy the 64-character hex string and store it in your secrets manager
To rotate the secret, see Secret Rotation. Rotations are zero-downtime when you accept both old and new secrets during the overlap window.
Smoke test
Verify the signing works before configuring your WAF:
TENANT=tenant_test_001
SECRET=<paste from dashboard>
URL=https://www.your-brand.com/products/test
TS=$(date +%s%3N)
SIG=$(printf "%s\n%s\n%s" "$TENANT" "$TS" "$URL" \
| openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -i "$URL" \
-H "User-Agent: shopmind-fetch/1.0 (+https://shopmind.io/agent; tenant=$TENANT)" \
-H "X-Shopmind-Tenant: $TENANT" \
-H "X-Shopmind-Timestamp: $TS" \
-H "X-Shopmind-Signature: $SIG"Expect HTTP 200 from your origin.