Votes API¶
!!! info "TL;DR" Vote on submissions to show support. Toggle pattern: POST to add vote, DELETE to remove. One vote per user per submission.
Vote on Submission¶
Add a vote to a submission. If the user has already voted, this operation is idempotent (no error).
Authentication: Required
Example Request:
curl -X POST https://canviq.app/api/submissions/550e8400-e29b-41d4-a716-446655440000/vote \
-H "Authorization: Bearer your-api-key"
Example Response:
{
"data": {
"submission_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "uuid",
"vote_count": 43,
"created_at": "2026-02-10T15:30:00Z"
}
}
Side Effects:
- Increments
vote_counton the submission (via database trigger) - Creates a follow relationship (user follows the submission)
- Sends notification to submission author
Remove Vote¶
Remove a vote from a submission.
Authentication: Required
Example Request:
curl -X DELETE https://canviq.app/api/submissions/550e8400-e29b-41d4-a716-446655440000/vote \
-H "Authorization: Bearer your-api-key"
Example Response:
Side Effects:
- Decrements
vote_counton the submission (via database trigger) - Does not remove the follow relationship (user continues following)
Get User's Votes¶
Get all submissions the authenticated user has voted for.
Authentication: Required
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 50) |
Example Request:
Example Response:
{
"data": [
{
"submission_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Dark mode support",
"status": "planned",
"vote_count": 42,
"voted_at": "2026-01-20T10:00:00Z"
},
{
"submission_id": "550e8400-e29b-41d4-a716-446655440001",
"title": "API access",
"status": "in_progress",
"vote_count": 67,
"voted_at": "2026-01-18T14:30:00Z"
}
],
"meta": {
"total": 15
}
}
Get Submission Voters¶
Get users who voted for a submission (admin only).
Authentication: Required (admin)
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page | number | Page number |
limit | number | Items per page |
Example Request:
curl https://canviq.app/api/submissions/550e8400-e29b-41d4-a716-446655440000/voters \
-H "Authorization: Bearer admin-api-key"
Example Response:
{
"data": [
{
"user_id": "uuid",
"name": "Jane Doe",
"email": "jane@example.com",
"voted_at": "2026-02-10T10:00:00Z"
}
],
"meta": {
"total": 42
}
}
Realtime Vote Updates¶
Subscribe to realtime vote count changes via Supabase Realtime:
import { createBrowserClient } from '@/lib/supabase/client'
const supabase = createBrowserClient()
const channel = supabase
.channel(`votes:submission=${submissionId}`)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'votes',
filter: `submission_id=eq.${submissionId}`,
},
(payload) => {
console.log('Vote change:', payload)
// Update UI with new vote count
}
)
.subscribe()
The UI updates in real time as other users vote.
Rate Limits¶
| Endpoint | Limit | Window |
|---|---|---|
POST /vote | 30 | 1 hour |
DELETE /vote | 30 | 1 hour |
GET /votes | 100 | 1 minute |
Voting is rate-limited to prevent abuse. If a user votes excessively, they'll receive a 429 Too Many Requests response.
Business Rules¶
- One vote per user per submission
- Voting automatically creates a follow relationship
- Removing a vote does not remove the follow (user still gets updates)
- Authors automatically vote for their own submissions on creation
- Deleted submissions cascade delete all votes
What's Next¶
- Comments API — Discuss submissions
- Follows — Notification preferences
- Realtime Updates — How realtime subscriptions work