X API Account Activity
Before X delivers account activity events to your webhook, it must confirm that you own the endpoint. It does this with a Challenge-Response Check: a GET request carrying a crc_token that your server must sign with your consumer secret using HMAC-SHA256 and return within 3 seconds.
X repeats this check roughly every 24 hours. If your endpoint fails, the webhook is marked invalid and event delivery stops until it passes again.
Everything runs locally in your browser using the Web Crypto API. Nothing is sent anywhere. Never paste a production secret into any website; use the demo values here.
Paste a crc_token and press Compute.
const crypto = require('crypto');
app.get('/webhook', (req, res) => {
const hmac = crypto
.createHmac('sha256', process.env.CONSUMER_SECRET)
.update(req.query.crc_token)
.digest('base64');
res.json({ response_token: 'sha256=' + hmac });
});CRC validation proves two things at once: that your endpoint is alive and responsive, and that whoever operates it holds the app's consumer secret. Because only your app and X know the secret, a correct HMAC signature is cryptographic proof of ownership. This prevents attackers from registering webhook URLs they do not control and stops X from delivering sensitive account activity to dead or hijacked endpoints.
Common failure causes: responding slower than 3 seconds, hashing the wrong value, using an API key instead of the consumer secret, hex-encoding instead of base64, or omitting the sha256= prefix in the JSON body.