Skip to main content
Groups are subsets of athletes within an organization. Coaches use groups to deliver targeted training plans, track collective stats, and manage workloads for specific cohorts (e.g. a Tuesday speed group, a marathon block cohort). All group endpoints require a valid user authentication token.

Create a group

POST /api/v1/groups
Authentication: Bearer token (require_user_auth)
name
string
required
Name for the group. Must be unique within the organization.
user_id
string
required
User ID of the group owner (typically the authenticated coach).
description
string
Optional description of the group’s purpose.
privacy
string
default:"public"
Visibility setting. One of "public" or "private".
org_id
string
Organization ID. Automatically resolved from the authenticated user’s profile if omitted.
curl -X POST https://api.astral.run/api/v1/groups \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Marathon Block A",
    "user_id": "usr_coach1",
    "description": "12-week marathon training cohort",
    "privacy": "private"
  }'
Response 200 OK
{
  "group_id": "grp_abc123",
  "status": "success"
}

Get a group

Returns details for a specific group. The authenticated user must be a member of the group or have a coach/admin role.
GET /api/v1/groups/{group_id}
Authentication: Bearer token
group_id
string
required
The unique group identifier.
curl https://api.astral.run/api/v1/groups/grp_abc123 \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "group_id": "grp_abc123",
  "name": "Marathon Block A",
  "description": "12-week marathon training cohort",
  "privacy": "private",
  "owner_id": "usr_coach1",
  "organization_id": "org_xyz789",
  "members": ["usr_athlete1", "usr_athlete2"],
  "created_at": "2025-04-01T08:00:00Z"
}

Update a group

Updates group metadata and membership. Membership changes are audit-logged automatically.
PUT /api/v1/groups/{group_id}
Authentication: Bearer token
group_id
string
required
The unique group identifier.
name
string
New group name.
description
string
Updated description.
privacy
string
Updated privacy setting: "public" or "private".
members
array
Full replacement member list (array of user ID strings). Additions and removals are detected automatically and recorded in the audit log.
curl -X PUT https://api.astral.run/api/v1/groups/grp_abc123 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "12-week marathon training — wave A",
    "members": ["usr_athlete1", "usr_athlete2", "usr_athlete3"]
  }'
Response 200 OK
{
  "group_id": "grp_abc123",
  "status": "success"
}

Delete a group

Permanently deletes a group. The deletion is audit-logged.
DELETE /api/v1/groups/{group_id}
Authentication: Bearer token
group_id
string
required
The unique group identifier.
curl -X DELETE https://api.astral.run/api/v1/groups/grp_abc123 \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "group_id": "grp_abc123",
  "status": "success"
}

List groups

Returns groups visible to the authenticated user. Coaches receive all groups belonging to their organization. Regular athletes receive only the groups they own.
GET /api/v1/groups
Authentication: Bearer token
page_size
integer
default:"20"
Number of groups to return.
curl "https://api.astral.run/api/v1/groups?page_size=50" \
  -H "Authorization: Bearer <token>"
Response 200 OK When the caller is a coach the response also includes organization-level summary fields.
{
  "groups": [
    {
      "group_id": "grp_abc123",
      "name": "Marathon Block A",
      "privacy": "private",
      "members": ["usr_athlete1", "usr_athlete2"],
      "organization_id": "org_xyz789"
    }
  ],
  "count": 1,
  "page_size": 50,
  "total_athletes": 40,
  "unassigned_count": 12
}
total_athletes
integer
Total active athletes in the organization (coaches only).
unassigned_count
integer
Number of active athletes not assigned to any group (coaches only).

Get group activities

Returns completed activities for all members of a group, suitable for rendering an activity feed on the group detail page.
GET /api/v1/groups/{group_id}/activities
Authentication: Bearer token (group member or coach)
group_id
string
required
The unique group identifier.
limit
integer
default:"20"
Number of activities to return.
start_date
string
Inclusive start date filter in YYYY-MM-DD format.
end_date
string
Inclusive end date filter in YYYY-MM-DD format.
sub_type
string
Comma-separated list of activity sub-types to filter (e.g. "easy-run,tempo").
curl "https://api.astral.run/api/v1/groups/grp_abc123/activities?limit=10&start_date=2025-04-01&end_date=2025-04-30" \
  -H "Authorization: Bearer <token>"

Get group stats

Returns aggregated training statistics for the group over a time range.
GET /api/v1/groups/{group_id}/stats
Authentication: Bearer token
group_id
string
required
The unique group identifier.
range
string
default:"7d"
Preset range: 7d, 14d, 30d, 60d, 90d, or custom.
from_ts
integer
Unix timestamp for the range start. Required when range=custom.
to_ts
integer
Unix timestamp for the range end. Required when range=custom.
timezone
string
IANA timezone string for date bucketing (e.g. "America/New_York"). Defaults to the authenticated user’s profile timezone.
curl "https://api.astral.run/api/v1/groups/grp_abc123/stats?range=30d" \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "summary": {
    "total_distance": 842500,
    "total_activities": 68,
    "engagement_rate": 85
  },
  "history": [
    {
      "date": "2025-04-01",
      "formattedDate": "2025-04-01",
      "usr_athlete1": {
        "distance": 12.5,
        "duration": 67.2,
        "vdot_load": 38.1,
        "nexrex_ete_plus": 42.0
      }
    }
  ],
  "member_names": {
    "usr_athlete1": "Alex"
  }
}
summary.total_distance
number
Total distance in meters across all members in the period.
summary.total_activities
integer
Total number of completed activities.
summary.engagement_rate
integer
Percentage of group members who logged at least one activity in the period.
history
array
Daily breakdown. Each entry has a date key plus one key per active user ID containing distance (km), duration (minutes), vdot_load, and nexrex_ete_plus (training load score).

Error codes

StatusMeaning
400Validation error — missing or invalid fields
401Missing or invalid authentication
403Access denied — not a group member, coach, or admin
404Group not found
409Conflict — a group with this name already exists
500Internal server error