Skip to main content

Sponsored Poll Tracking

Sponsored poll bid responses include signed vote_url and results_url values. Use those URLs exactly as returned by the bid response — the signed token authorizes access to a rendered poll instance and keeps clients from voting against or reading raw poll IDs.
Voting records poll engagement only. It does not redirect to the advertiser. If the poll response includes cta_url, show that CTA after voting and use it for advertiser click-throughs.

Vote

POST /api/v1/tracking/sponsored-polls/vote?token=...
Submit a user’s selected poll option. The vote is recorded and the endpoint returns the current lifetime aggregate results for the poll.

Request

token
string
required
Signed token from the poll creative’s vote_url. It is already present in the vote_url query string — POST to that URL exactly as returned. The token must be in the query string; it is not read from the request body.
option_id
string
required
Selected option identifier from the poll creative’s options[].id.
{
  "option_id": "0"
}

Response

The response is wrapped in a status / data envelope. data holds the current aggregate results.
{
  "status": "success",
  "data": {
    "sponsored_poll_id": 123,
    "question": "What kind of playlist are you making?",
    "total_votes": 42,
    "options": [
      { "id": "0", "text": "Wedding", "votes": 21, "percentage": 50 },
      { "id": "1", "text": "Study", "votes": 14, "percentage": 33.3 },
      { "id": "2", "text": "Workout", "votes": 7, "percentage": 16.7 }
    ]
  }
}
status
string
Always "success" for a recorded (or deduplicated) vote.
data
object
Current aggregate poll results.

Duplicate Votes

Each signed poll token records at most one vote for its bid/impression. A duplicate vote attempt does not increment the aggregate counts and is not an error — the endpoint returns 200 with the current aggregate results, exactly like a first vote. There is no separate duplicate-vote status code, so clients do not need special handling; the returned data is always safe to render.

Results

GET /api/v1/tracking/sponsored-polls/results?token=...
Retrieve current aggregate results without recording a vote. Use this endpoint to refresh percentages after rendering, or when the user returns to an already-voted poll.

Query Parameters

token
string
required
Signed token from the poll creative’s results_url.

Response

Same status / data envelope as the vote endpoint.
{
  "status": "success",
  "data": {
    "sponsored_poll_id": 123,
    "question": "What kind of playlist are you making?",
    "total_votes": 42,
    "options": [
      { "id": "0", "text": "Wedding", "votes": 21, "percentage": 50 },
      { "id": "1", "text": "Study", "votes": 14, "percentage": 33.3 },
      { "id": "2", "text": "Workout", "votes": 7, "percentage": 16.7 }
    ]
  }
}
status
string
Always "success".
data
object
Current aggregate poll results — sponsored_poll_id, question, total_votes, and an options array of id / text / votes / percentage rows.

Error Handling

Errors use the standard FastAPI shape — a single detail string:
{
  "detail": "Invalid poll option"
}
Status CodeScenariodetailClient Behavior
400 Bad Requestoption_id does not match one of the poll optionsInvalid poll optionKeep the poll visible and let the user choose a valid option
404 Not FoundToken is malformed, expired, or not valid for this pollLink expired or not foundDo not retry with the same token; request a fresh poll creative
422 Unprocessable EntityRequired token query parameter or option_id body field is missingFastAPI validation detailFix the request; this indicates a client integration bug
500 Internal Server ErrorTracking service failed unexpectedlyInternal Server ErrorContinue without blocking the chat experience
The 422 response body uses FastAPI’s validation format (detail is an array of field errors), not a single string.

Rendering Flow

  1. Render the poll question and option buttons from the bid response.
  2. Fire the impression pixel — extract the view_url query parameter from cta_url (or use the top-level view_url field when the poll has no cta_url) and request it when the poll becomes visible.
  3. When the user selects an option, POST option_id to vote_url.
  4. Replace the option buttons with the returned percentages and total vote count (read from the data object).
  5. If cta_url is present, reveal the advertiser CTA after the vote.
  6. Use results_url to refresh percentages without recording another vote.