GET /surveys/{surveyId}/panelists/{panelistId}
Retrieve panelist access information and generate a secure voting URL.
Request
GET /tenant/surveys/{surveyId}/panelists/{panelistId}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| surveyId | string | The survey ID (from your Votito dashboard) |
| panelistId | string | Your unique identifier for the panelist (e.g., customer ID, email hash) |
Headers
| Header | Required | Description |
|---|---|---|
| X-API-Key | Yes | Your API key |
Response
{
"panelist": {
"canRespond": true,
"sessionCount": 0,
"activityStartedUnixTs": null,
"activityMostRecentUnixTs": null,
"dismissedMostRecentUnixTs": null,
"publicKey": "abc123:xyz789",
"hostedResponseUrl": "https://www.votito.com/app/panelist/vote/?key=abc123:xyz789"
},
"survey": {
"status": "OPEN",
"title": "Customer Satisfaction Survey"
}
}
Response Fields
panelist object
| Field | Type | Description |
|---|---|---|
| canRespond | boolean | Whether the panelist can currently respond |
| sessionCount | integer | Number of times the panelist has started a session |
| activityStartedUnixTs | integer|null | Unix timestamp of first activity, or null if no activity |
| activityMostRecentUnixTs | integer|null | Unix timestamp of most recent activity |
| dismissedMostRecentUnixTs | integer|null | Unix timestamp of most recent survey dismissal, or null if never dismissed |
| publicKey | string | Compound token for Panelist API authentication |
| hostedResponseUrl | string | Ready-to-use voting URL for the panelist |
survey object
| Field | Type | Description |
|---|---|---|
| status | string | Survey status: “DRAFT”, “OPEN”, or “CLOSED” |
| title | string | Survey title |
Examples
cURL
curl -X GET \
"https://api.votito.com/tenant/surveys/survey-123/panelists/customer-456" \
-H "X-API-Key: vtt_your_api_key"
JavaScript
const getSurveyAccess = async (surveyId, panelistId) => {
const response = await fetch(
`https://api.votito.com/tenant/surveys/${surveyId}/panelists/${panelistId}`,
{
headers: { 'X-API-Key': process.env.VOTITO_API_KEY }
}
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
};
// Usage
const { panelist, survey } = await getSurveyAccess('survey-123', 'customer-456');
console.log(`Voting URL: ${panelist.hostedResponseUrl}`);
Python
import requests
import os
def get_survey_access(survey_id, panelist_id):
response = requests.get(
f"https://api.votito.com/tenant/surveys/{survey_id}/panelists/{panelist_id}",
headers={"X-API-Key": os.environ["VOTITO_API_KEY"]}
)
response.raise_for_status()
return response.json()
# Usage
data = get_survey_access("survey-123", "customer-456")
print(f"Voting URL: {data['panelist']['hostedResponseUrl']}")
Common Use Cases
Survey Distribution
Generate unique voting URLs for each panelist in your CRM:
const response = await fetch(
`https://api.votito.com/tenant/surveys/${surveyId}/panelists/${customerId}`,
{ headers: { 'X-API-Key': apiKey } }
);
const { panelist } = await response.json();
// Send panelist.hostedResponseUrl to your customer
sendEmail(customer.email, panelist.hostedResponseUrl);
Survey Fatigue Prevention
Check if a panelist has already responded or recently dismissed before showing surveys:
const { panelist } = await response.json();
if (!panelist.canRespond) {
// Don't show survey - already responded or survey closed
return;
}
// Check if panelist dismissed recently (e.g., within 30 days)
if (panelist.dismissedMostRecentUnixTs) {
const thirtyDaysAgo = Math.floor(Date.now() / 1000) - (30 * 24 * 60 * 60);
if (panelist.dismissedMostRecentUnixTs > thirtyDaysAgo) {
// Don't show survey - dismissed less than 30 days ago
return;
}
}
if (panelist.sessionCount > 0) {
// Panelist has already started responding
// You may want to limit repeated invitations
}
Activity Tracking
Monitor when panelists interact with surveys:
const { panelist } = await response.json();
console.log(`First activity: ${new Date(panelist.activityStartedUnixTs * 1000)}`);
console.log(`Last activity: ${new Date(panelist.activityMostRecentUnixTs * 1000)}`);
console.log(`Sessions: ${panelist.sessionCount}`);
Error Handling
| Status | Error | Description |
|---|---|---|
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | API key belongs to a different tenant |
| 404 | survey_not_found | Survey does not exist or belongs to another tenant |
Notes
Panelist ID Best Practices
- Use stable, unique identifiers (customer IDs, hashed emails)
- Avoid personally identifiable information in plain text
- Keep IDs consistent across requests for accurate activity tracking
URL Expiration
The hostedResponseUrl does not expire. The publicKey is valid as long as:
- The survey remains OPEN
- The panelist hasn’t exhausted their response limit
Multiple Responses
If allowMultipleResponses is enabled on the survey, canRespond will be true even after the panelist has responded.