Me
Overview
The Me module exposes the authenticated user's own profile, onboarding state, and personal game listings. All endpoints are scoped to the caller via JWT — no user ID is passed in the path.
Onboarding is handled by this module:
GET /api/v1/me— checkhasCompletedOnboardingto decide whether to route the user to the onboarding screen.POST /api/v1/me— complete the one-time onboarding by submitting the required profile fields.
The client checks onboarding status on every app launch via GET /api/v1/me and redirects incomplete users to the onboarding screen.
Profile Fields
| Field | Type | Validation | Remarks |
|---|---|---|---|
name | string | Required, non-blank, max 200 characters | |
contactMethods | array of ContactMethod | At least one valid contact method (whatsapp, telegram, or messenger), max 10 entries | |
skillLevels | array of SkillLevelSelection | Required, non-null, max 50 entries; at most one entry per activity | Each entry pairs an activity with the user's skill level for that activity |
ContactMethod
| Field | Type | Description |
|---|---|---|
name | string | One of whatsapp, telegram, messenger |
value | string | The contact handle or number; must be non-blank |
SkillLevelSelection
| Field | Type | Description |
|---|---|---|
activityId | UUID | The activity this selection applies to (see Activity) |
skillLevelId | long | A skill level from GET /api/v1/skill-levels?activityId=... that belongs to activityId |
Only one skill level per activity is allowed. Listing the same activityId twice returns 409 Conflict with error code DUPLICATE_SKILL_LEVEL_ACTIVITY.
API Contract
All endpoints are under /api/v1/me and require a valid JWT. Responses are wrapped in a standard ApiResponse envelope:
{
"success": true,
"data": { ... },
"message": "...",
"timestamp": "2026-04-04T12:00:00Z",
"path": "/api/v1/me"
}
GET /api/v1/me
Returns the current user profile and onboarding state. Called on every app launch.
cURL
curl -X GET http://localhost:8080/api/v1/me \
-H "Authorization: Bearer <TOKEN>"
Response 200 OK
{
"success": true,
"data": {
"userId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"authProvider": "auth0",
"providerUuid": "auth0|abc123",
"name": null,
"contactMethods": [],
"skillLevels": [],
"hasCompletedOnboarding": false,
"totalGamesJoined": 0,
"totalGamesHosted": 0,
"penaltyPoints": 0
},
"message": "User profile retrieved successfully.",
"timestamp": "2026-04-04T12:00:00Z",
"path": "/api/v1/me"
}
hasCompletedOnboarding: false— redirect to the onboarding screen.hasCompletedOnboarding: true— proceed to the main app.
POST /api/v1/me
Completes the user profile. This is a one-time operation — calling it again after onboarding is complete returns 409 Conflict.
cURL
curl -X POST http://localhost:8080/api/v1/me \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"contactMethods": [
{ "name": "whatsapp", "value": "+6591234567" },
{ "name": "telegram", "value": "@janedoe" }
],
"skillLevels": [
{ "activityId": "550e8400-e29b-41d4-a716-446655440000", "skillLevelId": 3 }
]
}'
Request body
{
"name": "Jane Doe",
"contactMethods": [
{ "name": "whatsapp", "value": "+6591234567" },
{ "name": "telegram", "value": "@janedoe" }
],
"skillLevels": [
{ "activityId": "550e8400-e29b-41d4-a716-446655440000", "skillLevelId": 3 }
]
}
Response 201 Created
{
"success": true,
"data": {
"userId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"authProvider": "auth0",
"providerUuid": "auth0|abc123",
"name": "Jane Doe",
"contactMethods": [
{ "name": "whatsapp", "value": "+6591234567" },
{ "name": "telegram", "value": "@janedoe" }
],
"skillLevels": [
{ "id": 3, "name": "High Beginner", "sortOrder": 3 }
],
"hasCompletedOnboarding": true,
"totalGamesJoined": 0,
"totalGamesHosted": 0,
"penaltyPoints": 0
},
"message": "User profile completed successfully.",
"timestamp": "2026-04-04T12:00:00Z",
"path": "/api/v1/me"
}
PUT /api/v1/me
Updates the authenticated user's profile. This endpoint uses patch semantics — only provided (non-null) fields are updated; omitted fields are left unchanged.
cURL
curl -X PUT http://localhost:8080/api/v1/me \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "John Updated",
"contactMethods": [
{ "name": "whatsapp", "value": "+6587654321" }
],
"skillLevels": [
{ "activityId": "550e8400-e29b-41d4-a716-446655440000", "skillLevelId": 5 }
]
}'
Request body
{
"name": "John Updated",
"contactMethods": [
{ "name": "whatsapp", "value": "+6587654321" }
],
"skillLevels": [
{ "activityId": "550e8400-e29b-41d4-a716-446655440000", "skillLevelId": 5 }
]
}
All fields are optional — only include the fields you want to update.
| Field | Type | Description |
|---|---|---|
name | string | Updated display name (max 200 chars) |
contactMethods | array of ContactMethod | Replaces all contact methods (max 10 entries) |
skillLevels | array of SkillLevelSelection | Replaces all skill levels (max 50 entries; one per activity) |
Response 200 OK
{
"success": true,
"data": {
"userId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"authProvider": "auth0",
"providerUuid": "auth0|abc123",
"name": "John Updated",
"contactMethods": [
{ "name": "whatsapp", "value": "+6587654321" }
],
"skillLevels": [
{ "id": 5, "name": "Middle Intermediate", "sortOrder": 5 }
],
"hasCompletedOnboarding": true,
"totalGamesJoined": 4,
"totalGamesHosted": 1,
"penaltyPoints": 0
},
"message": "User profile updated successfully.",
"timestamp": "2026-04-06T12:00:00Z",
"path": "/api/v1/me"
}
DELETE /api/v1/me
Permanently deletes the authenticated user's account and all associated data. This action is irreversible.
cURL
curl -X DELETE http://localhost:8080/api/v1/me \
-H "Authorization: Bearer <TOKEN>"
Response 204 No Content
No response body.
Me Games
Cursor-based listings scoped to the authenticated user. Both endpoints return CursorPagedResponse<GameResponse>, sorted by startTime ascending. See Game for the response shape.
GET /api/v1/me/games/joined
Games where the caller is an ACTIVE participant (excludes games they organise).
curl -X GET "http://localhost:8080/api/v1/me/games/joined?size=20" \
-H "Authorization: Bearer <TOKEN>"
GET /api/v1/me/games/hosted
Games where the caller is the organiser.
curl -X GET "http://localhost:8080/api/v1/me/games/hosted?size=20" \
-H "Authorization: Bearer <TOKEN>"
Query parameters (both endpoints)
| Parameter | Type | Validation |
|---|---|---|
cursor | string | Opaque cursor from a previous response |
size | integer | 1..1000, default 20 |
Sequence Diagram
Happy Path
Already Completed
Duplicate Completion Attempt
Error Handling
| Scenario | HTTP Status | Error Code | Client Behavior |
|---|---|---|---|
| Missing or invalid required fields | 400 | Validation error | Highlight invalid fields, stay on screen |
| Profile already completed | 409 | PROFILE_ALREADY_COMPLETED | Redirect to main app |
| Multiple skill levels for same activity | 409 | DUPLICATE_SKILL_LEVEL_ACTIVITY | Collapse selections; only one per activity |
| Skill level does not belong to its activity | 400 | Validation error | Re-fetch the activity's skill levels |
| Auth token expired / missing | 401 | — | Redirect to login |
| User not found (any endpoint) | 404 | — | Redirect to login |
| Server error | 500 | — | Show retry prompt |
Implementation Notes
- The client must call
GET /api/v1/meon every cold start — never cache onboarding state locally, as it may be reset server-side. - Profile completion is a single atomic operation, not a multi-step flow. All required fields (
name,contactMethods,skillLevels) must be submitted together. - Skill level selections are scoped per activity: each entry in
skillLevelsmust pair anactivityIdwith askillLevelIdthat belongs to that activity, and each activity may appear at most once. - At least one contact method with a valid name (
whatsapp,telegram, ormessenger) and a non-blank value is required. - After completion, profile updates are done via
PUT /api/v1/me(see API contract above). Only non-null fields are updated. DELETE /api/v1/mepermanently removes the user account — this action is irreversible.