Vote Webhooks and Rewards
Get notified when someone votes, verify the request, and reward your voters
Payload
Set a webhook URL in your bot settings. We send a JSON POST for each vote.
1{2 "event": "bot.vote",3 "event_id": "5b0d6c1e-7c1f-4a67-9d2e-0f4c1f0b6a11",4 "is_test": false,5 "vote_timestamp": "2026-01-31T12:34:56.000Z",6 "bot_id": "000000000000000000",7 "bot_name": "ExampleBot",8 "voter_id": "000000000000000000",9 "voter_username": "User",10 "votes": 42,11 "weight": 1,12 "voter_total_votes": 3,13 "voter_streak": 3,14 "reward_hint": "Supporter role",15 "bot_page_url": "https://labs.conscherry.com/bots/000000000000000000"16}
event_idis stable across retries. Use it to ignore duplicates.weightis 2 on weekends (UTC) and 1 otherwise. Use it if you want weekend votes to count double in your own rewards.reward_hintis the best matching reward tier you configured for that voter.new_vote_countis still sent as a string for older integrations. Prefervotes.
Verify the signature
When a secret is set, every request carries X-Labs-Timestamp (unix seconds) and X-Labs-Signature, a hex HMAC SHA-256 of the timestamp, a dot, and the raw request body. Reject requests with an old timestamp to stop replays.
1import { createHmac, timingSafeEqual } from 'node:crypto';23export function verify(rawBody, headers, secret) {4 const timestamp = headers['x-labs-timestamp'];5 const given = String(headers['x-labs-signature'] || '').replace('sha256=', '');6 if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;7 const expected = createHmac('sha256', secret)8 .update(timestamp + '.' + rawBody)9 .digest('hex');10 const a = Buffer.from(given);11 const b = Buffer.from(expected);12 return a.length === b.length && timingSafeEqual(a, b);13}
Authorization: Bearer [secret] so existing endpoints keep working. Move to signature checks when you can.Retries and pausing
- Respond with any 2xx within 8 seconds to confirm delivery.
- Timeouts, network errors, 408, 429 and 5xx responses are retried after 30 seconds, 2 minutes, 10 minutes, 1 hour and 6 hours.
- Other 4xx responses and redirects are not retried.
- After 20 deliveries in a row fail for good, the webhook is paused and you are notified. Fix your endpoint and resume it in your bot settings, or save a new URL.
- Settings shows the last 20 deliveries and lets you resend any that failed.
- Webhook URLs must be public. Private and local addresses are rejected.
Check whether a user voted
Requires an API key with read:stats that belongs to the bot owner.
1GET https://labs.conscherry.com/api/v1/bots/{botId}/check?userId={discordUserId}2Authorization: Bearer YOUR_API_KEY34{5 "success": true,6 "data": {7 "botId": "000000000000000000",8 "userId": "000000000000000000",9 "voted": true,10 "lastVotedAt": "2026-01-31T12:34:56.000Z",11 "nextVoteAt": "2026-02-01T00:34:56.000Z",12 "totalVotes": 3,13 "weight": 1,14 "rewardHint": "Supporter role"15 }16}
voted is true while the voter is still inside the 12 hour window. Limit: 120 requests per minute per key.
Live status
Your bot shows a green dot while we are hearing from it. Posting stats counts automatically. If you do not post stats often, send a heartbeat instead (needs write:stats, once a minute at most). A bot counts as online for 45 minutes after its last stats post or heartbeat, and as seen today for 24 hours.
1POST https://labs.conscherry.com/api/v1/heartbeat2Authorization: Bearer YOUR_API_KEY3Content-Type: application/json45{ "botId": "000000000000000000" }
Voter rewards
Add up to five reward tiers in settings, for example 1 vote for a supporter role and 10 votes for premium commands. Tiers appear on your bot page and in reward_hint. Voters also build a daily streak that earns bonus molecules at 3, 7, 14, 30, 60, 100 and 365 days, and can ask for a reminder when they can vote again.
Need more help?
Ask in our Discord or email support@conscherry.com.