API Reference
Complete API documentation and SDK reference for VOTR integration
π Interactive API Documentation
For a complete interactive API documentation with live testing capabilities, visit our Swagger documentation:
π VOTR API Swagger Documentation
The Swagger documentation provides:
- Interactive API endpoint testing
- Detailed request/response schemas
- Authentication examples
- Real-time API exploration
OAuth Token
API Endpoint
POST https://api-dev.govotr.com/api/v1/oauth/token (Development)
POST https://api.govotr.com/api/v1/oauth/token (Production)
Request Body
{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Response
{
"access_token": "eyJraWQiOiJjRFNKN2VsazhoZzdLcEVoakJLcFRNSHRZMlN2eXFTWEwyUUdJTHNNQm5JPSIsImFsZyI6IlJTMjU2In0...",
"token_type": "Bearer",
"expires_in": 3600
}
Important Notes
API Response Format:
- The API returns the voting URL in
response.data.url - Response uses
status(boolean) instead ofsuccess - The URL contains a JWT token as a query parameter
Usage in Code:
// Frontend calls YOUR backend API (not VOTR API directly)
// Option 1: Using email
const response = await fetch("/api/voting/generate-url", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userAuthToken}`, // Your app's user token
},
body: JSON.stringify({
email: userEmail,
eventId: eventId,
}),
})
// Option 2: Using account number
const response = await fetch("/api/voting/generate-url", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userAuthToken}`, // Your app's user token
},
body: JSON.stringify({
accountNo: userAccountNo,
eventId: eventId,
}),
})
const data = await response.json()
if (data.status) {
const votingUrl = data.data.url // This is what you pass to SDK
setVotingUrl(votingUrl)
}
Voting URL
Primary Endpoint (Recommended)
Get Ballot by Event and Account:
GET https://api-dev.govotr.com/api/v1/events/:eventId/ballot (Development)
GET https://api.govotr.com/api/v1/events/:eventId/ballot (Production)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
eventId |
string |
β | 24-character hexadecimal event ID (ObjectId) |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
accountNo |
string |
β | Account number of the shareholder |
brokerId |
string |
β | 24-character hexadecimal broker ID |
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Requirements
- Valid OAuth 2.0 access token with appropriate scope
- Account number and broker ID must be provided (both required)
- Account must be registered as a shareholder for the specified event
- Event must be in active voting period
- Voting must be live for the event
- Event must be a Proxy event type
Success Response
{
"message": "Data retrieved successfully",
"status": true,
"data": {
"url": "https://vote.govotr.com/public-voting-screen?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
Response Fields:
| Field | Type | Description |
|---|---|---|
message |
string |
Success message |
status |
boolean |
Request status (true for success) |
data.url |
string |
Voting URL with authentication token |
Token Validity:
- Tokens are valid for 1 hour
- Should be used within that timeframe
- After expiration, a new token must be requested
Error Responses
400 - Bad Request (Validation Error):
{
"status": false,
"error": "Account number is required"
}
{
"status": false,
"error": "Broker ID is required"
}
{
"status": false,
"error": "Invalid event ID format"
}
401 - Unauthorized:
{
"status": false,
"error": "Invalid or missing authentication token"
}
403 - Forbidden:
{
"status": false,
"error": "You do not have permission to access this resource"
}
404 - Not Found:
{
"status": false,
"error": "Event not found"
}
{
"status": false,
"error": "Account not found for this event"
}
{
"status": false,
"error": "Ballot not found"
}
423 - Locked (Resource Unavailable):
{
"status": false,
"error": "Voting URL Not Active"
}
{
"status": false,
"error": "Voting period has not started yet"
}
{
"status": false,
"error": "Event is not a Proxy event type"
}
429 - Too Many Requests:
{
"status": false,
"error": "Rate limit exceeded. Please try again later"
}
500 - Internal Server Error:
{
"status": false,
"error": "Internal server error"
}
Usage Example
cURL:
curl -X GET "https://api-dev.govotr.com/api/v1/events/64f8a1b2c3d4e5f6a7b8c9d0/ballot?accountNo=ACC123456&brokerId=681a3f923b07005fdc5b5417" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
JavaScript/Node.js:
async function getVotingUrl(accessToken, eventId, accountNo, brokerId) {
const url = `https://api-dev.govotr.com/api/v1/events/${eventId}/ballot?accountNo=${accountNo}&brokerId=${brokerId}`
try {
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
})
const data = await response.json()
if (data.status) {
console.log("Voting URL:", data.data.url)
return data.data.url
} else {
console.error("Error:", data.error)
return null
}
} catch (error) {
console.error("Network error:", error)
return null
}
}
// Usage
const votingUrl = await getVotingUrl(
"your_access_token",
"64f8a1b2c3d4e5f6a7b8c9d0",
"ACC123456",
"681a3f923b07005fdc5b5417"
)
Python:
import requests
def get_voting_url(access_token, event_id, account_no, broker_id):
url = f"https://api-dev.govotr.com/api/v1/events/{event_id}/ballot"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
params = {
"accountNo": account_no,
"brokerId": broker_id
}
try:
response = requests.get(url, headers=headers, params=params)
data = response.json()
if response.status_code == 200 and data.get('status'):
print(f"Voting URL: {data['data']['url']}")
return data['data']['url']
else:
print(f"Error: {data.get('error', 'Unknown error')}")
return None
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
return None
# Usage
voting_url = get_voting_url(
'your_access_token',
'64f8a1b2c3d4e5f6a7b8c9d0',
'ACC123456',
'681a3f923b07005fdc5b5417'
)
Deprecated Endpoint β οΈ
β οΈ DEPRECATED: The
/ballot/by-emailendpoint is deprecated and will be removed in a future version. Please migrate to the newGET /api/v1/events/:eventId/ballotendpoint.
POST https://api-dev.govotr.com/api/v1/ballot/by-email (Development) - DEPRECATED
POST https://api.govotr.com/api/v1/ballot/by-email (Production) - DEPRECATED
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Request Body (Deprecated Endpoint)
Option 1: Using Email
{
"email": "john.doe@example.com",
"eventId": "64f8a1b2c3d4e5f6a7b8c9d0"
}
Option 2: Using Account Number
{
"accountNo": "ACC123456789",
"eventId": "64f8a1b2c3d4e5f6a7b8c9d0"
}
Option 3: Using Control Number (Deprecated)
{
"controlNo": "CTRL123456",
"eventId": "64f8a1b2c3d4e5f6a7b8c9d0"
}
Note:
controlNoreferences have been removed from all new endpoints. This option is only available in the deprecated endpoint.
Important Notes:
- Either
email,accountNo, orcontrolNomust be provided (only one, not multiple) - The provided identifier must be registered as a shareholder for the specified event
- Token expires after 1 hour
- Event must be live/active for voting
Response (Deprecated Endpoint)
Success Response:
{
"message": "Data retrieved successfully",
"status": true,
"data": {
"url": "http://localhost:5173/public-voting-screen?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
Error Responses (Deprecated Endpoint)
Shareholder not found by email:
{
"status": false,
"error": "email: john.doe@example.com not found for this event"
}
Shareholder not found by account number:
{
"status": false,
"error": "account number: ACC123456 not found for this event"
}
Shareholder not found by control number:
{
"status": false,
"error": "control number: CTRL123456 not found for this event"
}
Voting not active:
{
"status": false,
"error": "Voting URL Not Active"
}
Voting period has ended:
{
"status": false,
"error": "Voting period has ended"
}
π For complete backend implementation examples, please refer to: Sample Repository
SDK Component
β This step is implemented in your React Native frontend.
Basic Usage
Once you have the voting URL, pass it to the SDK component:
import React, { useState } from "react"
import { StyleSheet, Alert } from "react-native"
import { VoteNowButton } from "@govotr/vote-react-native"
export default function VotingComponent() {
const [votingUrl, setVotingUrl] = useState<string>("")
const [isLoading, setIsLoading] = useState<boolean>(false)
const handleVoteClick = async () => {
setIsLoading(true)
try {
// Call YOUR backend API to get voting URL
// You can use either email or accountNo
const requestBody = userEmail
? { email: userEmail, eventId: eventId }
: { accountNo: userAccountNo, eventId: eventId }
const response = await fetch("https://your-api.com/vote", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(requestBody),
})
const data = await response.json()
if (data.status && data.data.url) {
setVotingUrl(data.data.url)
} else {
Alert.alert("Error", "Failed to get voting URL")
}
} catch (error) {
console.error("Vote API error:", error)
} finally {
setIsLoading(false)
}
}
const styles = StyleSheet.create({
voteButton: {
backgroundColor: "#5263FF",
borderRadius: 12,
alignItems: "center",
},
voteButtonText: {
color: "white",
fontSize: 18,
fontWeight: "bold",
},
})
return (
<VoteNowButton
URL={votingUrl}
label="Vote Now"
buttonStyle={styles.voteButton}
textStyle={styles.voteButtonText}
onPress={handleVoteClick}
isLoading={isLoading}
onSuccess={() => {
setVotingUrl("") // Clear URL after successful vote
}}
onBack={() => {
setVotingUrl("") // Clear URL when user goes back
}}
onError={(error) => {
console.log(error)
Alert.alert("Error", "Voting failed")
}}
/>
)
}
Advanced Usage with Custom Styling
<VoteNowButton
URL={votingUrl}
label="Cast Your Vote"
buttonStyle={{
backgroundColor: "#007AFF",
borderRadius: 8,
paddingVertical: 12,
paddingHorizontal: 24,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
}}
textStyle={{
color: "white",
fontSize: 16,
fontWeight: "bold",
}}
onSuccess={() => {
Alert.alert("Success", "Your vote has been recorded!")
}}
onError={(error) => {
Alert.alert("Error", `Voting failed: ${error}`)
}}
/>
Complete Working Example
Hereβs a full working example showing exactly how to use the VoteNowButton SDK:
import React, { useState } from "react"
import { View, Alert, StyleSheet, Text } from "react-native"
import { VoteNowButton } from "@govotr/vote-react-native"
export default function VotingScreen() {
const [votingUrl, setVotingUrl] = useState<string>("")
const [isLoading, setIsLoading] = useState<boolean>(false)
// Function to handle vote API call
const handleVoteClick = async () => {
setIsLoading(true)
try {
// Call YOUR backend API to get voting URL
// Prepare request body with either email or accountNo
const requestBody = {
eventId: "64f8a1b2c3d4e5f6a7b8c9d0", // Replace with actual eventId
...(userEmail ? { email: userEmail } : { accountNo: userAccountNo }),
}
const response = await fetch("http://your-api-url.com/vote", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
})
const data = await response.json()
if (data.status && data.data.url) {
setVotingUrl(data.data.url)
console.log("Vote URL received:", data.data.url)
} else {
Alert.alert("Error", data.error || "Failed to get voting URL")
}
} catch (error) {
console.error("Vote API error:", error)
Alert.alert("Error", "Unable to start voting. Please try again.")
} finally {
setIsLoading(false)
}
}
return (
<View style={styles.container}>
<Text style={styles.title}>Annual General Meeting 2024</Text>
<Text style={styles.subtitle}>Shareholder Voting</Text>
<VoteNowButton
URL={votingUrl ? votingUrl : ""}
label="Vote Now"
buttonStyle={styles.voteButton}
textStyle={styles.voteButtonText}
onPress={handleVoteClick}
isLoading={isLoading}
onSuccess={() => {
setVotingUrl("") // Clear URL after successful vote
Alert.alert("Success", "Your vote has been recorded successfully!")
}}
onBack={() => {
setVotingUrl("") // Clear URL when user goes back
console.log("User closed voting modal")
}}
onError={(error) => {
console.log(error)
Alert.alert(
"Voting Error",
"An error occurred while voting. Please try again."
)
}}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
justifyContent: "center",
alignItems: "center",
},
title: {
fontSize: 24,
fontWeight: "bold",
textAlign: "center",
marginBottom: 10,
},
subtitle: {
fontSize: 18,
textAlign: "center",
marginBottom: 30,
color: "#666",
},
voteButton: {
backgroundColor: "#5263FF",
borderRadius: 12,
alignItems: "center",
minWidth: 200,
},
voteButtonText: {
color: "white",
fontSize: 18,
fontWeight: "bold",
textAlign: "center",
},
successMessage: {
fontSize: 18,
color: "green",
textAlign: "center",
fontWeight: "bold",
},
})
SDK Props Reference
VoteNowButton Props
| Prop | Type | Required | Description |
|---|---|---|---|
URL |
string |
β | The voting URL obtained from VOTR API |
label |
string |
β | Button text (default: βVote Nowβ) |
buttonStyle |
ViewStyle |
β | Custom styling for the button |
textStyle |
TextStyle |
β | Custom styling for the button text |
isDisabled |
boolean |
β | Whether the button is disabled |
onPress |
() => void |
β | Called when button is pressed |
onSuccess |
() => void |
β | Called when voting completes successfully |
onError |
(error: string) => void |
β | Called when an error occurs |
onBack |
() => void |
β | Called when user closes the voting modal |
Example with All Props
<VoteNowButton
URL={votingUrl}
label="Submit Your Vote"
buttonStyle={{
backgroundColor: "#28a745",
borderRadius: 12,
paddingVertical: 16,
paddingHorizontal: 32,
elevation: 3,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
}}
textStyle={{
color: "#ffffff",
fontSize: 18,
fontWeight: "600",
textAlign: "center",
}}
isDisabled={!votingUrl}
onPress={() => console.log("Vote button pressed")}
onSuccess={() => {
Alert.alert("Success", "Vote submitted successfully!")
navigation.goBack()
}}
onError={(error) => {
console.error("Voting error:", error)
Alert.alert("Error", "Failed to submit vote. Please try again.")
}}
onBack={() => {
console.log("User cancelled voting")
}}
/>
Error Handling
Common Error Scenarios
<VoteNowButton
URL={votingUrl}
label="Vote Now"
onError={(error) => {
let userMessage = "An error occurred while voting. Please try again."
if (error.includes("network")) {
userMessage =
"Network connection lost. Please check your internet connection."
} else if (error.includes("timeout")) {
userMessage = "Voting session timed out. Please try again."
} else if (error.includes("permission")) {
userMessage = "You do not have permission to access this voting session."
}
Alert.alert("Voting Error", userMessage)
setVotingUrl("") // Clear URL to close modal
}}
/>
Test Data
Download Test Data
For your convenience, we provide test data that you can use to test your integration:
Sample Test Data
Use the following test data to verify your API integration:
Test Data with Email:
| EventId | Expected Response | |
|---|---|---|
68da919c6837407211b64db5 |
lindsey@govotr.com |
Vote now Url Generated successfully |
68cbe3fc76a057049a2cf7b4 |
maham@govotr.com |
Vote now Url Generated successfully |
68cbe3fc76a057049a2cf7b4 |
hamza@govotr.com |
Email not found for this event |
68cbe3fc76a057049a2cf7b4 |
affan@govotr.com |
Vote now Url Generated successfully |
68cbe68876a057049a2cf945 |
maham@govotr.com |
Vote now Url Generated successfully |
68cbe68876a057049a2cf945 |
hamza@govotr.com |
Email not found for this event |
68cbe68876a057049a2cf945 |
affan@govotr.com |
Vote now Url Generated successfully |
68cbe7dd76a057049a2cfa79 |
Any email | Voting period has ended |
Test Data with Account Number:
| EventId | Account Number | Expected Response |
|---|---|---|
68da919c6837407211b64db5 |
ACC001234567890 |
Vote now Url Generated successfully |
68cbe3fc76a057049a2cf7b4 |
34646800 |
Vote now Url Generated successfully |
68cbe3fc76a057049a2cf7b4 |
34646819 |
Account not found for this event |
68cbe3fc76a057049a2cf7b4 |
64410242 |
Vote now Url Generated successfully |
68cbe68876a057049a2cf945 |
79818786 |
Vote now Url Generated successfully |
68cbe68876a057049a2cf945 |
12450920 |
Account not found for this event |
68cbe68876a057049a2cf945 |
24449208 |
Vote now Url Generated successfully |
68cbe7dd76a057049a2cfa79 |
Any account number | Voting period has ended |
How to Use Test Data
- Copy an EventId from the table above
- Use the corresponding email, account number, or control number for testing
- Call the Voting URL API with these parameters
- Verify the response matches the expected result
Example API Call with Email:
curl -X POST "https://api-dev.govotr.com/api/v1/ballot/by-email" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"eventId": "68cbe3fc76a057049a2cf7b4",
"email": "maham@govotr.com"
}'
Example API Call with Account Number:
curl -X POST "https://api-dev.govotr.com/api/v1/ballot/by-email" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"eventId": "68cbe3fc76a057049a2cf7b4",
"accountNo": "34646800"
}'
Example API Call with Control Number:
curl -X POST "https://api-dev.govotr.com/api/v1/ballot/by-email" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"eventId": "68cbe3fc76a057049a2cf7b4",
"controlNo": "CTRL002"
}'
Expected Response:
{
"message": "Data retrieved successfully",
"status": true,
"data": {
"url": "https://vote.govotr.com/public-voting-screen?token=..."
}
}
Next Step: Troubleshooting Guide for common issues and solutions.