Skip to main content
The users endpoints expose the full Astral user profile. All endpoints require a valid Bearer token. A user can always read and update their own profile. Reading another user’s full profile (GET /users/{user_id}) requires admin access — public profile data is available through the dedicated public endpoint.

GET /api/v1/users/

Get a user’s complete profile in the canonical schema. You can only retrieve your own profile with a standard user token. Admin tokens may retrieve any user.
curl https://app.nexrex.ai/api/v1/users/uid_abc123 \
  -H "Authorization: Bearer <access_token>"

Path parameters

user_id
string
required
The user’s unique ID.

Response

{
  "status": "ok",
  "data": {
    "user_id": "uid_abc123",
    "email": "runner@example.com",
    "username": "runner",
    "display_name": "Alex Runner",
    "first_name": "Alex",
    "last_name": "Runner",
    "bio": "Marathon runner based in Taipei.",
    "profile_image_url": "https://storage.example.com/avatars/uid_abc123.jpg",
    "status": "active",
    "membership_role": "athlete",
    "is_verified": true,
    "created_at": 1745395200000,
    "updated_at": 1745395200000,
    "last_login_at": 1745481600000,
    "preferences": {
      "language": "en",
      "timezone": "Asia/Taipei",
      "unit_system": "metric",
      "coach_persona": "rex",
      "workout_days": ["monday", "wednesday", "friday"]
    },
    "subscription": {
      "tier": "free",
      "status": "active",
      "started_at": null,
      "expires_at": null
    },
    "integrations": {
      "strava": {"connected": false},
      "garmin": {"connected": false},
      "apple_health": {"connected": false}
    },
    "notification_settings": {
      "leaderboard_like": true,
      "new_activity_like": true,
      "new_activity_comment": true,
      "new_follower": true,
      "ai_coach_notification": true,
      "email_notification": true,
      "activity_completed_notification": true,
      "workout_reminder": true,
      "weekly_summary": true
    },
    "privacy": {
      "share_activity_in_profile": true,
      "share_activity_with_coach": true,
      "share_activity_with_public": false,
      "share_profile_info": true,
      "share_race_goal": true,
      "share_pb": true,
      "share_pace_data": true,
      "share_race_history": true,
      "share_activity_map": true,
      "paticipate_leaderboard": true
    }
  }
}
user_id
string
The user’s unique Firebase UID.
email
string
The user’s email address.
username
string
Unique username. 3–30 alphanumeric characters and underscores.
display_name
string
Public display name. Up to 50 characters. Does not need to be unique.
first_name
string
First name.
last_name
string
Last name.
bio
string
Short biography displayed on the public profile.
profile_image_url
string
URL of the user’s avatar image.
status
string
Account status. One of active, inactive, suspended, pending, or deactivated.
membership_role
string
Primary role. "athlete" or "coach".
is_verified
boolean
Whether the user’s email has been verified.
created_at
integer
Account creation time as a Unix epoch millisecond timestamp.
updated_at
integer
Last profile update time as a Unix epoch millisecond timestamp.
last_login_at
integer
Last login time as a Unix epoch millisecond timestamp.
preferences
object
User preferences including language, timezone, unit system, AI coach persona, and preferred workout days.
subscription
object
Subscription details including tier (free, etc.), status, and expiry dates.
integrations
object
Connection status for Strava, Garmin, and Apple Health.
notification_settings
object
Per-event notification toggles (all boolean).
privacy
object
Per-field privacy settings controlling what other users can see (all boolean).

PUT /api/v1/users/

Update a user’s profile. You can only update your own profile. Sends a partial or complete profile object — only the keys you include are updated. Nested structures like integrations, zones, subscription, notification_settings, and privacy are also accepted.
curl -X PUT https://app.nexrex.ai/api/v1/users/uid_abc123 \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Alex R.",
    "bio": "5k and 10k runner.",
    "notification_settings": {
      "workout_reminder": false,
      "weekly_summary": true
    }
  }'

Path parameters

user_id
string
required
The user’s unique ID. Must match the authenticated user’s ID.

Request body

Send any combination of the writable profile fields. The following top-level fields are accepted:
display_name
string
Public display name. Maximum 50 characters.
first_name
string
First name.
last_name
string
Last name.
bio
string
Short biography. Length limit enforced server-side.
profile_image_url
string
URL to the user’s avatar image.
preferences
object
Preference fields to update. See PATCH /users/me/preferences for field-level validation rules.
notification_settings
object
Notification preferences. All values must be boolean.
privacy
object
Privacy settings. All values must be boolean.
integrations
object
Integration connection state. Accepted providers: strava, garmin, apple_health, coros. Each must include a connected boolean.
zones
object
Training zones including heartrate, pace, and vdot sub-objects.
subscription
object
Subscription data. Status must be one of active, inactive, cancelled, expired, trialing.

Response

{
  "status": "ok",
  "data": {
    "message": "Profile updated successfully",
    "updated_fields": ["display_name", "bio", "notification_settings"]
  }
}
updated_fields
array
List of top-level field keys that were included in the update payload.

GET /api/v1/users//public

Get the privacy-filtered public profile of any user. The fields returned depend on the target user’s privacy settings.
curl https://app.nexrex.ai/api/v1/users/uid_abc123/public \
  -H "Authorization: Bearer <access_token>"

Path parameters

user_id
string
required
The user ID of the profile to view.

Response

{
  "status": "ok",
  "data": {
    "user_id": "uid_abc123",
    "username": "runner",
    "display_name": "Alex Runner",
    "profile_image_url": "https://storage.example.com/avatars/uid_abc123.jpg",
    "bio": "Marathon runner based in Taipei.",
    "created_at": 1745395200000
  }
}
user_id
string
The user’s unique ID.
username
string
The user’s username. Always present.
display_name
string
Public display name. Always present.
profile_image_url
string
Avatar URL. Only included when the user’s share_profile_info privacy setting is true.
bio
string
Biography. Only included when share_profile_info is true.
created_at
integer
Account creation time as Unix epoch milliseconds.

PATCH /api/v1/users/me/preferences

Update the authenticated user’s preferences. Only the fields you send are updated — this is a partial update. Unrecognized fields are logged and ignored.
curl -X PATCH https://app.nexrex.ai/api/v1/users/me/preferences \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "unit_system": "imperial",
    "timezone": "America/New_York",
    "language": "en"
  }'

Request body

unit_system
string
"metric" or "imperial".
timezone
string
A valid IANA timezone string (e.g. "Asia/Taipei", "America/New_York").
language
string
Locale code. Supported values: "en", "zh-TW", "ja".
coach_persona
string
AI coach persona. "rex" or "joy".
workout_days
array
Preferred training days. Array of lowercase weekday strings: "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday".
nexrex_lab
boolean
Enable or disable beta features.

Response

{
  "status": "ok",
  "data": {
    "message": "Preferences updated successfully",
    "updated_fields": ["unit_system", "timezone"],
    "preferences": {
      "unit_system": "imperial",
      "timezone": "America/New_York"
    }
  }
}
updated_fields
array
List of preference keys that were successfully updated.
preferences
object
The preference values that were applied.

PUT /api/v1/users/username

Update the authenticated user’s username. Usernames must be unique across the platform.
curl -X PUT https://app.nexrex.ai/api/v1/users/username \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"username": "new_username"}'

Request body

username
string
required
The new username. 3–30 characters, letters, numbers, and underscores only. Must not already be in use.

Response

{
  "status": "ok",
  "message": "Username updated successfully",
  "username": "new_username"
}

GET /api/v1/users/check-username

Check whether a username is available before attempting to claim it.
curl "https://app.nexrex.ai/api/v1/users/check-username?username=alex_runner" \
  -H "Authorization: Bearer <access_token>"

Query parameters

username
string
required
The username to check. Must meet the format requirements (3–30 characters, alphanumeric and underscores).

Response

{
  "status": "ok",
  "available": true,
  "username": "alex_runner"
}
available
boolean
true if the username is not taken, false if it is already in use.
username
string
The username that was checked.