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

URL Expiration

The hostedResponseUrl does not expire. The publicKey is valid as long as:

Multiple Responses

If allowMultipleResponses is enabled on the survey, canRespond will be true even after the panelist has responded.