# Route Spec

## Route ID
`posts-vote-distribution-get`

## Endpoint
`GET /api/v1/posts/{postId}/vote/distribution`

## Human Description
Returns anonymous detailed 5-position vote distribution for a votable post frame. Distribution is hidden until the post has at least 3 votes.

## Authentication
- Required: `yes`

## Responses
### Success: `200 OK`
```json
{
  "success": true,
  "message": "Vote distribution loaded",
  "data": {
    "totalVotes": 288,
    "resultsVisible": true,
    "minimumVotesToShow": 3,
    "buckets": [
      {"position": "strong_left", "bucket": "left", "count": 34},
      {"position": "left", "bucket": "left", "count": 121},
      {"position": "neutral", "bucket": "neutral", "count": 41},
      {"position": "right", "bucket": "right", "count": 63},
      {"position": "strong_right", "bucket": "right", "count": 29}
    ]
  }
}
```

When fewer than 3 votes exist:
```json
{
  "success": true,
  "message": "Vote distribution hidden until more ratings are available",
  "data": {
    "totalVotes": 2,
    "resultsVisible": false,
    "minimumVotesToShow": 3,
    "buckets": []
  }
}
```

### Error: `401 Unauthorized`
When returned:
- Missing or invalid access token.

Body:
```json
{"success": false, "error": {"code": "UNAUTHORIZED", "message": "Authentication required.", "details": {}}}
```

### Error: `404 Not Found`
When returned:
- Post does not exist.

Body:
```json
{"success": false, "error": {"code": "POST_NOT_FOUND", "message": "Post does not exist.", "details": {}}}
```

## Data & Caching Dependencies
- **Spanner Tables:** `post_vote_counters, post_vote_bucket_counters (Read)`
- **Redis Cache:** `None`
- **GCS Storage:** `None`
- **Edge Cache (CDN):** `No`

## Side Effects
- None (read-only endpoint).

## Visibility Rules
- Non-slider posts are not votable and return `POST_NOT_VOTABLE`.
- Distribution is visible only when `totalVotes >= 3`.
- Under 3 votes, `buckets` is an empty array.
- This does not affect `GET /api/v1/posts/{postId}/vote/me`, which returns only the current user's vote status and never returns their selected position.
- Vote values are read only from anonymous aggregate counters, not from user-linked vote receipts.
