Summary
REST endpoints for concert attendance tracking. Thin wrappers around extrachill-users data functions, following the abilities-first pattern.
Endpoints
1. Toggle Attendance
POST /extrachill/v1/concert-tracking/toggle
Toggle mark/unmark for an event. Returns new state + count.
Request:
{
"event_id": 123,
"blog_id": 7
}
Response (marked):
{
"marked": true,
"count": 13,
"count_label": "13 going",
"timing": "upcoming"
}
Response (unmarked):
{
"marked": false,
"count": 12,
"count_label": "12 going",
"timing": "upcoming"
}
Permission: is_user_logged_in()
Implementation: Delegates to ec_users_toggle_event() from extrachill-users.
2. Get Event Attendance
GET /extrachill/v1/concert-tracking/event/{event_id}
Get attendance info for a specific event (public).
Response:
{
"count": 47,
"count_label": "47 were there",
"timing": "past",
"user_marked": true,
"attendees": [
{ "user_id": 1, "display_name": "Chubes", "avatar_url": "..." },
{ "user_id": 5, "display_name": "Zach U", "avatar_url": "..." }
]
}
Permission: Public read (__return_true), user_marked requires auth.
3. Get User Shows
GET /extrachill/v1/concert-tracking/user/{user_id}/shows
Paginated concert history for a user.
Query params:
page (default 1)
per_page (default 20)
period — upcoming | past | all (default all)
year — filter by year (e.g. 2025)
date_from, date_to — custom date range
Response:
{
"shows": [
{
"event_id": 123,
"title": "Goose at Moody Center",
"event_date": "2026-04-05",
"venue": "Moody Center",
"city": "Austin",
"artists": ["Goose"],
"timing": "upcoming",
"thumbnail": "..."
}
],
"total": 47,
"pages": 3,
"page": 1
}
Permission: Own shows = is_user_logged_in(), other users = public (for future public profiles).
4. Get User Stats
GET /extrachill/v1/concert-tracking/user/{user_id}/stats
Aggregate stats for a user's concert history.
Query params:
year — filter by year
date_from, date_to — custom date range
Response:
{
"total_shows": 47,
"unique_venues": 23,
"unique_artists": 38,
"unique_cities": 12,
"top_artists": [
{ "name": "Goose", "slug": "goose", "count": 5 },
{ "name": "Billy Strings", "slug": "billy-strings", "count": 4 }
],
"top_venues": [
{ "name": "Moody Center", "slug": "moody-center", "count": 8 },
{ "name": "Stubb's", "slug": "stubbs", "count": 6 }
],
"top_cities": [
{ "name": "Austin", "slug": "austin", "count": 22 },
{ "name": "Charleston", "slug": "charleston", "count": 15 }
],
"shows_by_year": {
"2026": 12,
"2025": 25,
"2024": 10
}
}
Permission: Own stats = is_user_logged_in(), other users = public.
Implementation
New file: inc/routes/concert-tracking.php
All handlers follow the thin-wrapper pattern:
register_rest_route( 'extrachill/v1', '/concert-tracking/toggle', [
'methods' => 'POST',
'callback' => function( $request ) {
$event_id = absint( $request['event_id'] );
$blog_id = absint( $request->get_param( 'blog_id' ) ?: get_current_blog_id() );
$user_id = get_current_user_id();
$result = ec_users_toggle_event( $user_id, $event_id, $blog_id );
$count = ec_users_get_event_mark_count( $event_id, $blog_id );
$timing = ec_users_get_event_timing( $event_id );
return rest_ensure_response( [
'marked' => $result['marked'],
'count' => $count,
'count_label' => ec_users_format_count_label( $count, $timing ),
'timing' => $timing,
] );
},
'permission_callback' => 'is_user_logged_in',
] );
Route file auto-discovered by extrachill-api's RecursiveIteratorIterator.
Acceptance Criteria
Dependencies
Related
Summary
REST endpoints for concert attendance tracking. Thin wrappers around extrachill-users data functions, following the abilities-first pattern.
Endpoints
1. Toggle Attendance
POST /extrachill/v1/concert-tracking/toggleToggle mark/unmark for an event. Returns new state + count.
Request:
{ "event_id": 123, "blog_id": 7 }Response (marked):
{ "marked": true, "count": 13, "count_label": "13 going", "timing": "upcoming" }Response (unmarked):
{ "marked": false, "count": 12, "count_label": "12 going", "timing": "upcoming" }Permission:
is_user_logged_in()Implementation: Delegates to
ec_users_toggle_event()from extrachill-users.2. Get Event Attendance
GET /extrachill/v1/concert-tracking/event/{event_id}Get attendance info for a specific event (public).
Response:
{ "count": 47, "count_label": "47 were there", "timing": "past", "user_marked": true, "attendees": [ { "user_id": 1, "display_name": "Chubes", "avatar_url": "..." }, { "user_id": 5, "display_name": "Zach U", "avatar_url": "..." } ] }Permission: Public read (
__return_true),user_markedrequires auth.3. Get User Shows
GET /extrachill/v1/concert-tracking/user/{user_id}/showsPaginated concert history for a user.
Query params:
page(default 1)per_page(default 20)period—upcoming|past|all(defaultall)year— filter by year (e.g.2025)date_from,date_to— custom date rangeResponse:
{ "shows": [ { "event_id": 123, "title": "Goose at Moody Center", "event_date": "2026-04-05", "venue": "Moody Center", "city": "Austin", "artists": ["Goose"], "timing": "upcoming", "thumbnail": "..." } ], "total": 47, "pages": 3, "page": 1 }Permission: Own shows =
is_user_logged_in(), other users = public (for future public profiles).4. Get User Stats
GET /extrachill/v1/concert-tracking/user/{user_id}/statsAggregate stats for a user's concert history.
Query params:
year— filter by yeardate_from,date_to— custom date rangeResponse:
{ "total_shows": 47, "unique_venues": 23, "unique_artists": 38, "unique_cities": 12, "top_artists": [ { "name": "Goose", "slug": "goose", "count": 5 }, { "name": "Billy Strings", "slug": "billy-strings", "count": 4 } ], "top_venues": [ { "name": "Moody Center", "slug": "moody-center", "count": 8 }, { "name": "Stubb's", "slug": "stubbs", "count": 6 } ], "top_cities": [ { "name": "Austin", "slug": "austin", "count": 22 }, { "name": "Charleston", "slug": "charleston", "count": 15 } ], "shows_by_year": { "2026": 12, "2025": 25, "2024": 10 } }Permission: Own stats =
is_user_logged_in(), other users = public.Implementation
New file:
inc/routes/concert-tracking.phpAll handlers follow the thin-wrapper pattern:
Route file auto-discovered by extrachill-api's
RecursiveIteratorIterator.Acceptance Criteria
function_exists()guard for extrachill-users functionsDependencies
Related