Trove API Reference

REST API 엔드포인트 문서 | Base URL: http://localhost:8080/api/v1

Table of Contents

Authentication

Most endpoints require JWT Bearer token authentication.

text
Authorization: Bearer <access_token>
  • Obtain tokens via POST /api/v1/auth/login or POST /api/v1/auth/register
  • Access tokens expire after a configured duration
  • Use POST /api/v1/auth/refresh with a refresh token to get new tokens
  • Endpoints marked with Auth: Required need a valid Bearer token
  • Endpoints marked with Auth: Optional accept a token for personalized results
  • Endpoints marked with Auth: None are publicly accessible

Error Response Format

All errors follow a consistent envelope:

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "request validation failed",
    "details": { ... }
  }
}
HTTP StatusError CodeDescription
400BAD_REQUEST / VALIDATION_ERRORInvalid request parameters
401UNAUTHORIZEDMissing or invalid authentication
403FORBIDDENInsufficient permissions
404NOT_FOUNDResource not found
409CONFLICTResource already exists
429RATE_LIMITEDToo many requests
500INTERNAL_SERVER_ERRORServer-side error

Endpoints

System

MethodPathAuthDescription
GET/healthNoneRoot health check endpoint
GET/api/v1/healthNoneAPI v1 health check endpoint

`GET /health`

Description: Root health check endpoint

Auth Required: No

Example:

bash
curl -X GET "http://localhost:8080/api/v1/health"

`GET /api/v1/health`

Description: API v1 health check endpoint

Auth Required: No

Example:

bash
curl -X GET "http://localhost:8080/api/v1/health"

Auth (인증)

MethodPathAuthDescription
POST/api/v1/auth/registerNoneRegister
POST/api/v1/auth/loginNoneLogin
POST/api/v1/auth/refreshNoneRefresh Token
POST/api/v1/auth/logoutRequiredLogout
POST/api/v1/auth/change-passwordRequiredChange Password

`POST /api/v1/auth/register`

Description: Register

Auth Required: No

Request Body (application/json):

FieldTypeRequiredValidation
emailstringYesValid email; Max: 255
usernamestringYesMin: 3; Max: 50; Alphanumeric + underscore
passwordstringYesMin: 8; Max: 128
display_namestringYesMin: 1; Max: 100
preferred_languagestringNoOne of: ko, en, ja, zh_cn, zh_tw

Response (201):

json
{
  "success": true,
  "data": { /* AuthResponse */ }
}

Response data fields (AuthResponse):

FieldTypeDescription
userUserResponse
tokensTokenPair

Example:

bash
curl -X POST "http://localhost:8080/api/v1/auth/register" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "username": "Sample Name", "password": "password123", "display_name": "Sample Name", "preferred_language": "ko"}'

`POST /api/v1/auth/login`

Description: Login

Auth Required: No

Request Body (application/json):

FieldTypeRequiredValidation
emailstringYesValid email
passwordstringYes

Response (200):

json
{
  "success": true,
  "data": { /* AuthResponse */ }
}

Response data fields (AuthResponse):

FieldTypeDescription
userUserResponse
tokensTokenPair

Example:

bash
curl -X POST "http://localhost:8080/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "password123"}'

`POST /api/v1/auth/refresh`

Description: Refresh Token

Auth Required: No

Request Body (application/json):

FieldTypeRequiredValidation
refresh_tokenstringYes

Response (200):

json
{
  "success": true,
  "data": { /* TokenPair */ }
}

Response data fields (TokenPair):

FieldTypeDescription
access_tokenstring
refresh_tokenstring
expires_atintegerUnix timestamp

Example:

bash
curl -X POST "http://localhost:8080/api/v1/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "eyJ..."}'

`POST /api/v1/auth/logout`

Description: Logout

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/auth/logout" \
  -H "Authorization: Bearer $TOKEN"

`POST /api/v1/auth/change-password`

Description: Change Password

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
current_passwordstringYes
new_passwordstringYesMin: 8; Max: 128

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/auth/change-password" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"current_password": "password123", "new_password": "password123"}'

Users (사용자)

MethodPathAuthDescription
GET/api/v1/users/meRequiredGet Me
GET/api/v1/users/me/statsRequiredreturns the current user's stats
PATCH/api/v1/users/meRequiredUpdate Profile
GET/api/v1/users/:idNoneGet Profile
GET/api/v1/users/:id/profileNonereturns public profile with stats
GET/api/v1/users/:id/activitiesNonelist user's public activities
GET/api/v1/users/:id/achievementsNonelist user's earned badges
GET/api/v1/users/:id/stampsNonelist user's check-in stamps
POST/api/v1/users/:id/followRequiredFollow User
DELETE/api/v1/users/:id/followRequiredUnfollow User
GET/api/v1/users/me/local-legendsRequiredReturns trails where the authenticated user is the local legend

`GET /api/v1/users/me`

Description: Get Me

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* UserProfileResponse */ }
}

Response data fields (UserProfileResponse):

FieldTypeDescription
follower_countinteger
following_countinteger
achievement_countinteger

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/me" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/users/me/stats`

Description: returns the current user's stats

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* UserStatsResponse */ }
}

Response data fields (UserStatsResponse):

FieldTypeDescription
total_checkinsinteger
total_distance_kmnumber
total_activitiesinteger
total_xpinteger
levelinteger
current_streakinteger
longest_streakinteger
trails_completedinteger
achievements_earnedinteger
follower_countinteger
following_countinteger

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/me/stats" \
  -H "Authorization: Bearer $TOKEN"

`PATCH /api/v1/users/me`

Description: Update Profile

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
display_namestring?NoMin: 1; Max: 100
biostring?NoMax: 500
avatar_urlstring?NoValid URL
preferred_languagestring?NoOne of: ko, en, ja, zh_cn, zh_tw
fitness_levelstring?NoOne of: beginner, intermediate, advanced, expert
interestsstring[]NoMax: 20

Response (200):

json
{
  "success": true,
  "data": { /* UserResponse */ }
}

Response data fields (UserResponse):

FieldTypeDescription
iduuid
emailstring
usernamestring
display_namestring
avatar_urlstring?
biostring?
preferred_languagestring
fitness_levelstring
interestsstring[]
total_distance_kmnumber
total_checkinsinteger
levelinteger
xp_pointsinteger
current_streakinteger
longest_streakinteger
is_verifiedboolean
is_premiumboolean
created_atdatetime

Example:

bash
curl -X PATCH "http://localhost:8080/api/v1/users/me" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "Sample Name", "bio": "value", "avatar_url": "https://example.com/image.jpg", "preferred_language": "ko", "fitness_level": "beginner", "interests": ["item1", "item2"]}'

`GET /api/v1/users/:id`

Description: Get Profile

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/{id}"

`GET /api/v1/users/:id/profile`

Description: returns public profile with stats

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/{id}/profile"

`GET /api/v1/users/:id/activities`

Description: list user's public activities

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/{id}/activities"

`GET /api/v1/users/:id/achievements`

Description: list user's earned badges

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/{id}/achievements"

`GET /api/v1/users/:id/stamps`

Description: list user's check-in stamps

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/{id}/stamps"

`POST /api/v1/users/:id/follow`

Description: Follow User

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/users/{id}/follow" \
  -H "Authorization: Bearer $TOKEN"

`DELETE /api/v1/users/:id/follow`

Description: Unfollow User

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X DELETE "http://localhost:8080/api/v1/users/{id}/follow" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/users/me/local-legends`

Description: Returns trails where the authenticated user is the local legend

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/users/me/local-legends" \
  -H "Authorization: Bearer $TOKEN"

Trails (트레일)

MethodPathAuthDescription
GET/api/v1/trailsNoneSearch
GET/api/v1/trails/nearbyNoneFind Nearby
GET/api/v1/trails/:idNoneGet By ID
POST/api/v1/trailsRequiredCreate
GET/api/v1/trails/:id/checkpointsNoneGet Checkpoints
GET/api/v1/trails/:id/stagesNoneGet Stages
GET/api/v1/trails/:id/weatherNoneGet Weather
GET/api/v1/trails/quick-startNoneGet Quick Start Routes
GET/api/v1/trails/seasonalNoneGet Seasonal Recommendations
GET/api/v1/trails/time-awareNoneGet Time Aware Recommendations
GET/api/v1/trails/:id/reviewsNoneGet Trail Reviews
POST/api/v1/trails/:id/reviewsRequiredCreate Review
POST/api/v1/trails/:id/completeRequiredverify trail completion
GET/api/v1/trails/:id/storiesNoneGet By Trail ID
GET/api/v1/trails/:id/stories/nearbyNoneFind Nearby
GET/api/v1/trails/:id/segmentsNoneReturns all segments (구간) for a given trail
GET/api/v1/trails/:id/local-legendNoneReturns the user with the most check-ins on this trail (로컬 레전드)
GET/api/v1/trails/:id/packing-listNoneReturns a weather-based packing checklist for the specified trail

`GET /api/v1/trails`

Description: Search

Auth Required: No

Query Parameters:

FieldTypeRequiredValidation
qstringNoMax: 255
regionstringNoMax: 100
citystringNoMax: 100
difficultystringNoOne of: easy, moderate, hard, expert
min_distancenumber?No>= 0
max_distancenumber?No>= 0
seasonsstring[]NoOne of: spring, summer, autumn, winter
sort_bystringNoOne of: rating, distance, name, created_at
sort_orderstringNoOne of: asc, desc
pageintegerNoMin: 1
page_sizeintegerNoMin: 1; Max: 100

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails"

`GET /api/v1/trails/nearby`

Description: Find Nearby

Auth Required: No

Query Parameters:

FieldTypeRequiredValidation
latitudenumberYesValid latitude
longitudenumberYesValid longitude
radius_kmnumberNo> 0; Max: 100
limitintegerNoMin: 1; Max: 50

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/nearby"

`GET /api/v1/trails/:id`

Description: Get By ID

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}"

`POST /api/v1/trails`

Description: Create

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
name_kostringYesMax: 255
name_enstring?NoMax: 255
description_kostring?No
description_enstring?No
trail_systemstring?NoMax: 100
managing_organizationstring?NoMax: 200
regionstring?NoMax: 100
citystring?NoMax: 100
countrystringNoMax: 10
total_distance_kmnumber?No> 0
estimated_duration_minutesinteger?No> 0
elevation_gain_minteger?No
elevation_loss_minteger?No
max_elevation_minteger?No
min_elevation_minteger?No
difficultystringYesOne of: easy, moderate, hard, expert
surface_typestringYesOne of: paved, gravel, dirt, rock, mixed, sand, boardwalk
is_wheelchair_accessiblebooleanNo
is_pet_friendlybooleanNo
is_loopbooleanNo
best_seasonsstring[]NoOne of: spring, summer, autumn, winter
terrain_tagsstring[]NoMax: 20
thumbnail_urlstring?NoValid URL

Response (201):

json
{
  "success": true,
  "data": { /* TrailResponse */ }
}

Response data fields (TrailResponse):

FieldTypeDescription
iduuid
name_kostring
name_enstring?
description_kostring?
description_enstring?
trail_systemstring?
regionstring?
citystring?
countrystring
start_latitudenumber?
start_longitudenumber?
total_distance_kmnumber?
estimated_duration_minutesinteger?
elevation_gain_minteger?
elevation_loss_minteger?
difficultystring
surface_typestring
is_wheelchair_accessibleboolean
is_pet_friendlyboolean
is_loopboolean
route_shapestring?Added 2026-09-11. Both GET /trails/:id AND GET /trails (list) — unlike data_source/network_segments/segment_ids above, this is not detail-only. Verbatim trails.route_shape (S2c, migration 20260814035733): one of loop / out_and_back / point_to_point / network. Omitted when the row is not yet derived (NULL) or the derivation concluded unknown (판정 입력 없음) — both collapse to "no key", never surfaced as the literal string "unknown". is_loop above is unchanged and unrelated to this field's presence.
best_seasonsstring[]
terrain_tagsstring[]
thumbnail_urlstring?
rating_avgnumber
rating_countinteger
completion_countinteger
created_atdatetime
data_sourcestring?Added 2026-09-10 (ACHASAN-NETWORK-COURSE-COMBINE). GET /trails/:id only — list endpoints omit the key. Internal ingestion code (e.g. kfs_aggregated_v1); clients must branch on it, never display it.
network_segments[[[number,number]]]?Added 2026-09-10. GET /trails/:id only, present only when route_status="preparing". RAW 망(network) geometry — every sub-line, before Stage A hides it — same [[ [lon,lat], ... ], ...] shape as route_segments. NOT the render truth (there is no derived course yet); route_segments/route_coordinates stay empty for these rows, unchanged.
network_part_countinteger?Added 2026-09-10. len(network_segments), alongside it (same presence rule).
segment_idsstring[]?Added 2026-09-10. GET /trails/:id only, present only when the row's provenance_notes.segment_ids JSON array is set (curation lineage). Omitted, never an empty array, when absent.

Example:

bash
curl -X POST "http://localhost:8080/api/v1/trails" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name_ko": "Sample Name", "name_en": "Sample Name", "description_ko": "Sample text content", "description_en": "Sample text content", "trail_system": "value", "managing_organization": "value", "region": "value", "city": "value", "country": "value", "total_distance_km": 5.2, "estimated_duration_minutes": 60, "elevation_gain_m": 1, "elevation_loss_m": 1, "max_elevation_m": 20, "min_elevation_m": 1, "difficulty": "easy", "surface_type": "paved", "is_wheelchair_accessible": false, "is_pet_friendly": false, "is_loop": false, "best_seasons": ["item1", "item2"], "terrain_tags": ["item1", "item2"], "thumbnail_url": "https://example.com/image.jpg"}'

`GET /api/v1/trails/:id/checkpoints`

Description: Get Checkpoints

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/checkpoints"

`GET /api/v1/trails/:id/stages`

Description: Get Stages

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/stages"

`GET /api/v1/trails/:id/weather`

Description: Get Weather

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/weather"

`GET /api/v1/trails/quick-start`

Description: Get Quick Start Routes

Auth Required: No

Query Parameters:

FieldTypeRequiredValidation
latitudenumberYesValid latitude
longitudenumberYesValid longitude
time_minutesintegerYesMin: 15; Max: 480
activity_typestringNoOne of: hiking, walking, running, cycling, trail_running

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/quick-start"

`GET /api/v1/trails/seasonal`

Description: Get Seasonal Recommendations

Auth Required: No

Query Parameters:

FieldTypeRequiredValidation
latitudenumberYesValid latitude
longitudenumberYesValid longitude
radius_kmnumberNo> 0; Max: 100
limitintegerNoMin: 1; Max: 50

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/seasonal"

`GET /api/v1/trails/time-aware`

Description: Get Time Aware Recommendations

Auth Required: No

Query Parameters:

FieldTypeRequiredValidation
latitudenumberYesValid latitude
longitudenumberYesValid longitude
radius_kmnumberNo> 0; Max: 100
limitintegerNoMin: 1; Max: 50

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/time-aware"

`GET /api/v1/trails/:id/reviews`

Description: Get Trail Reviews

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/reviews"

`POST /api/v1/trails/:id/reviews`

Description: Create Review

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
ratingintegerYesMin: 1; Max: 5
difficulty_ratinginteger?NoMin: 1; Max: 5
scenery_ratinginteger?NoMin: 1; Max: 5
review_textstring?NoMax: 2000
photosstring[]NoMax: 5
visited_datestring?No
seasonstring?NoOne of: spring, summer, autumn, winter

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/trails/{id}/reviews" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rating": 5, "difficulty_rating": 5, "scenery_rating": 5, "review_text": "Sample text content", "photos": ["item1", "item2"], "visited_date": "2026-03-30", "season": "spring"}'

`POST /api/v1/trails/:id/complete`

Description: verify trail completion

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
activity_idstring?No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Response data fields — 보상 정책 (TrailCompletionResponse, 2026-09-10 신설 키 3):

FieldTypeDescription
xp_withheld_reasonstring?완주는 인정됐는데 XP 만 판정으로 나가지 않은 사유. 닫힌 어휘 recompletion_cooldown \daily_cap, 보류 없으면 null.
xp_rationumber정가 100 에 곱해진 비율. 첫 완주 1.0 · 쿨다운(30일) 지난 재완주 0.5 · 보류 0.0. xp_earned = 100 × xp_ratio 가 항상 성립.
recompletion_eligible_atstring? (RFC3339)xp_withheld_reason == "recompletion_cooldown" 일 때만 실린다. 이 완주가 그때 지급된다는 뜻이 아니다 — 보류는 종단이고 소급 지급이 없다. "그때 다시 걸으면 받는다"의 시각.

정책 요약 (결정서 docs/data/REWARD_POLICY_DECISION_2026-09-10.md §3):

  • 같은 트레일 첫 완주 = 100(전액).
  • 재완주: 직전 *실지급*(amount > 0) 뒤 30일 안이면 0 + recompletion_cooldown, 30일 지나면 50%(정수 내림). 세 번째부터도 같다.
  • 하루(KST) 상한 300 — 이 완주 라우트에만 적용된다. 종주 시트 완성(같은 source='trail_complete', 5000)은 상한 밖이다: 상한은 source 가 아니라 원장의 description("trail complete")으로 경로를 식별한다.
  • 보류는 종단이다. 완주 사실·스탬프·업적·집계는 그대로 나가고 XP 만 0 이다.

Example:

bash
curl -X POST "http://localhost:8080/api/v1/trails/{id}/complete" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"activity_id": "value"}'

`GET /api/v1/trails/:id/stories`

Description: Get By Trail ID

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/stories"

`GET /api/v1/trails/:id/stories/nearby`

Description: Find Nearby

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/stories/nearby"

`GET /api/v1/trails/:id/segments`

Description: Returns all segments (구간) for a given trail

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/segments"

`GET /api/v1/trails/:id/local-legend`

Description: Returns the user with the most check-ins on this trail (로컬 레전드)

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/local-legend"

`GET /api/v1/trails/:id/packing-list`

Description: Returns a weather-based packing checklist for the specified trail

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/trails/{id}/packing-list"

Check-ins (체크인)

MethodPathAuthDescription
POST/api/v1/checkinsRequiredCreate
POST/api/v1/checkins/photo-verifyRequiredverify a photo's EXIF GPS against a checkpoint. Form fields: - file: the ima...
GET/api/v1/checkins/meRequiredGet My Checkins
GET/api/v1/checkins/:idRequiredget single check-in detail
GET/api/v1/checkins/:id/verifyRequiredverify check-in status

`POST /api/v1/checkins`

Description: Create

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
checkpoint_iduuidYes
verification_methodstringYesOne of: gps, qr, photo, manual
latitudenumberYesValid latitude
longitudenumberYesValid longitude
accuracy_metersnumber?No>= 0
qr_code_valuestring?No
photo_urlstring?NoValid URL
photo_metadata*PhotoVerificationMetadataNo
notestring?NoMax: 500

Response (201):

json
{
  "success": true,
  "data": { /* CheckinResultResponse */ }
}

Response data fields (CheckinResultResponse):

FieldTypeDescription
checkinCheckinResponse
xp_earnedinteger
transport_modestring
new_achievements[]AchievementEarned
streak_infoStreakInfo
level_progressLevelProgressResponse
level_up*LevelUpResponse

Example:

bash
curl -X POST "http://localhost:8080/api/v1/checkins" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"checkpoint_id": "550e8400-e29b-41d4-a716-446655440000", "verification_method": "gps", "latitude": 37.5665, "longitude": 126.978, "accuracy_meters": 0.0, "qr_code_value": "value", "photo_url": "https://example.com/image.jpg", "note": "value"}'

`POST /api/v1/checkins/photo-verify`

Description: verify a photo's EXIF GPS against a checkpoint. Form fields: - file: the image file (required, multipart, JPEG only for EXIF) - checkpoint_id: the UUID of the checkpoint to verify against (required) The handler extracts GPS coordinates from the photo's EXIF data and compares them with the checkpoint location. If the photo was taken within 200m of the checkpoint, the check-in is automatically created

Auth Required: Yes (Bearer token)

Request (multipart/form-data):

FieldTypeRequiredDescription
filefileYesImage file (JPEG, PNG, HEIC, WebP)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/checkins/photo-verify" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg"

`GET /api/v1/checkins/me`

Description: Get My Checkins

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/checkins/me" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/checkins/:id`

Description: get single check-in detail

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/checkins/{id}" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/checkins/:id/verify`

Description: verify check-in status

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/checkins/{id}/verify" \
  -H "Authorization: Bearer $TOKEN"

Activities (활동)

MethodPathAuthDescription
POST/api/v1/activitiesRequiredCreate
GET/api/v1/activities/meRequiredGet My Activities
GET/api/v1/activities/summaryRequiredGet Summary
GET/api/v1/activities/:idRequiredGet By ID
GET/api/v1/activities/:id/gpxRequireddownload activity as GPX file
POST/api/v1/activities/:id/completeRequiredComplete
POST/api/v1/activities/:id/pauseRequiredPause
POST/api/v1/activities/:id/resumeRequiredResume
POST/api/v1/activities/:id/photosRequiredForm fields: - file: the image file (required, multipart) - latitude: GPS lat...
DELETE/api/v1/activities/:idRequiredDiscard

`POST /api/v1/activities`

Description: Create

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
trail_iduuid?No
activity_typestringYesOne of: hiking, walking, running, cycling, driving, trail_running
titlestring?NoMax: 255
is_publicbooleanNo

Response (201):

json
{
  "success": true,
  "data": { /* ActivityResponse */ }
}

Response data fields (ActivityResponse):

FieldTypeDescription
iduuid
user_iduuid
trail_iduuid?
activity_typestring
statusstring
started_atdatetime
finished_atdatetime?
duration_secondsinteger?
distance_kmnumber?
avg_speed_kmhnumber?
max_speed_kmhnumber?
elevation_gain_minteger?
elevation_loss_minteger?
calories_burnedinteger?
step_countinteger?
titlestring?
notesstring?
is_publicboolean
created_atdatetime

Example:

bash
curl -X POST "http://localhost:8080/api/v1/activities" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"trail_id": "550e8400-e29b-41d4-a716-446655440000", "activity_type": "hiking", "title": "Sample Title", "is_public": true}'

`GET /api/v1/activities/me`

Description: Get My Activities

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/activities/me" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/activities/summary`

Description: Get Summary

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/activities/summary" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/activities/:id`

Description: Get By ID

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/activities/{id}" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/activities/:id/gpx`

Description: download activity as GPX file

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/activities/{id}/gpx" \
  -H "Authorization: Bearer $TOKEN"

`POST /api/v1/activities/:id/complete`

Description: Complete

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
distance_kmnumber?No>= 0
elevation_gain_minteger?No
elevation_loss_minteger?No
calories_burnedinteger?No>= 0
step_countinteger?No>= 0
notesstring?NoMax: 1000

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Response data fields — 보상 정책 (ActivityResponse, 2026-09-10 신설 키 2 + 2026-09-11 신설 키 2):

FieldTypeDescription
xp_withheld_reasonstring?보상이 판정으로 0 이 된 사유. 자유 활동(트레일 미결박)은 below_min_distance(서버 실측 거리가 하한 미만, 궤적 없는 완료도 같은 값). 트레일 완주를 주장한 활동(영수증 정산 경로)은 recompletion_cooldown\daily_cap. 보류 없으면 키 부재(= null 로 읽는다).
reward_min_kmnumber?이 완료에 실제로 적용된 거리 하한(km). 하한이 걸리는 경로(자유 활동)에서만 실린다.
xp_rationumber?2026-09-11 신설. 트레일 완주를 주장한 활동이 영수증 정산기를 지났을 때만 실린다. 첫 완주 1.0 · 쿨다운 지난 재완주 0.5 · 보류(쿨다운/일일상한) 0.0. xp_earned = 100 × xp_ratio. 자유 활동에는 없다(nil).
recompletion_eligible_atstring? (RFC3339)2026-09-11 신설. xp_withheld_reason == "recompletion_cooldown" 일 때만 실린다. "이 완주가 그때 지급된다"가 아니라 "그때 다시 걸으면 받는다"의 시각 — 보류는 종단이고 소급 지급이 없다.

정책 요약 (결정서 §3):

  • 자유 활동(트레일 미결박) 완료 보상의 서버 실측 거리 하한: 0.05 km → 1.0 km. 미달이면 활동 기록은 저장되고 XP 만 0 이다.
  • 시간 조건은 없다. 서버가 도출한 활동 시간이 없고, 유일한 시간(duration_seconds)은 클라이언트 저작이라 보상 지표에 실을 수 없다.
  • 이어걷기 예외: 종주 구간 완주로 성립한 활동에는 하한이 적용되지 않는다. 그 활동은 트레일을 주장하므로 완주 영수증 정산기가 보상을 내고, 그 경로에는 거리 하한이 없다 — xp_withheld_reason/reward_min_km 은 그 응답에 실리지 않는다(대신 쿨다운/일일상한이 적용되면 xp_ratio/recompletion_eligible_at 이 실린다).
  • 모바일은 `POST /trails/:id/complete` 를 호출하지 않는다(2026-09-11 실측) — 트레일 완주를 주장한 활동의 재완주 쿨다운·일일상한 사실은 이 엔드포인트(ActivityResponse)로만 앱에 도달한다. TrailCompletionResponse(웹/관리 도구 전용)는 같은 사실을 같은 함수(rewardPolicyFieldsFromSettlement)로 낸다.
  • 네 키 모두 완주 응답에만 나타난다. 생성/목록 응답에는 없다.

Example:

bash
curl -X POST "http://localhost:8080/api/v1/activities/{id}/complete" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"distance_km": 5.2, "elevation_gain_m": 1, "elevation_loss_m": 1, "calories_burned": 1, "step_count": 1, "notes": "value"}'

`POST /api/v1/activities/:id/pause`

Description: Pause

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/activities/{id}/pause" \
  -H "Authorization: Bearer $TOKEN"

`POST /api/v1/activities/:id/resume`

Description: Resume

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/activities/{id}/resume" \
  -H "Authorization: Bearer $TOKEN"

`POST /api/v1/activities/:id/photos`

Description: Form fields: - file: the image file (required, multipart) - latitude: GPS latitude where the photo was taken (required) - longitude: GPS longitude where the photo was taken (required) - captured_at: ISO 8601 timestamp when the photo was taken (required) - caption: optional text caption for the photo Photos are stored in MinIO under the "activity" category with the activity ID prefix

Auth Required: Yes (Bearer token)

Request (multipart/form-data):

FieldTypeRequiredDescription
filefileYesImage file (JPEG, PNG, HEIC, WebP)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/activities/{id}/photos" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg"

`DELETE /api/v1/activities/:id`

Description: Discard

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X DELETE "http://localhost:8080/api/v1/activities/{id}" \
  -H "Authorization: Bearer $TOKEN"

Achievements (업적)

MethodPathAuthDescription
GET/api/v1/achievementsNoneGet All
GET/api/v1/achievements/meRequiredSupports ?status=earned/in_progress query parameter. Default: returns only ea...
GET/api/v1/achievements/:idNoneget single achievement detail

`GET /api/v1/achievements`

Description: Get All

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/achievements"

`GET /api/v1/achievements/me`

Description: Supports ?status=earned|in_progress query parameter. Default: returns only earned achievements (earned_at IS NOT NULL)

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/achievements/me" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/achievements/:id`

Description: get single achievement detail

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/achievements/{id}"

Missions (미션)

MethodPathAuthDescription
GET/api/v1/missionsNoneGet Missions
GET/api/v1/missions/meRequiredGet My Missions

`GET /api/v1/missions`

Description: Get Missions

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/missions"

`GET /api/v1/missions/me`

Description: Get My Missions

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/missions/me" \
  -H "Authorization: Bearer $TOKEN"

Gamification (게이미피케이션)

MethodPathAuthDescription
GET/api/v1/gamification/levelRequiredGet Level Progress
GET/api/v1/gamification/leaderboardRequiredGet Leaderboard

`GET /api/v1/gamification/level`

Description: Get Level Progress

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/gamification/level" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/gamification/leaderboard`

Description: Get Leaderboard

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* GroupLeaderboardResponse */ }
}

Response data fields (GroupLeaderboardResponse):

FieldTypeDescription
board_typestring
periodstring
group_rangestring
group_sizeinteger
my_group_rankinteger?
entries[]LeaderboardEntryResponse

Example:

bash
curl -X GET "http://localhost:8080/api/v1/gamification/leaderboard" \
  -H "Authorization: Bearer $TOKEN"

Leaderboard (리더보드)

MethodPathAuthDescription
GET/api/v1/leaderboardOptionalGet Leaderboard
GET/api/v1/leaderboard/meRequiredReturns the authenticated user's rank across all leaderboard categories

`GET /api/v1/leaderboard`

Description: Get Leaderboard

Auth Required: Optional (enhances response with user context)

Response (200):

json
{
  "success": true,
  "data": { /* GroupLeaderboardResponse */ }
}

Response data fields (GroupLeaderboardResponse):

FieldTypeDescription
board_typestring
periodstring
group_rangestring
group_sizeinteger
my_group_rankinteger?
entries[]LeaderboardEntryResponse

Example:

bash
curl -X GET "http://localhost:8080/api/v1/leaderboard"

`GET /api/v1/leaderboard/me`

Description: Returns the authenticated user's rank across all leaderboard categories

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* MyRankingResponse */ }
}

Response data fields (MyRankingResponse):

FieldTypeDescription
user_iduuid
usernamestring
display_namestring
levelinteger
total_xpinteger
global_rankinteger?
global_scorenumber
global_totalinteger?
weekly_rankinteger?
weekly_scorenumber
weekly_totalinteger?

Example:

bash
curl -X GET "http://localhost:8080/api/v1/leaderboard/me" \
  -H "Authorization: Bearer $TOKEN"

Stamps (스탬프)

MethodPathAuthDescription
GET/api/v1/stampsRequiredlist user's stamp collection
GET/api/v1/stamps/collectionsRequiredlist stamp collection sets grouped by trail
GET/api/v1/stamps/daily-bonusRequiredReturns the current daily bonus status including streak info, XP multiplier, ...
GET/api/v1/stamps/evolutionRequiredReturns stamp evolution progress and earned evolved stamps. Tracks three evol...
GET/api/v1/stamps/encountersRequiredReturns all random encounter stamps found by the user. Includes hidden stamps...
GET/api/v1/stamps/gamificationRequiredReturns a combined overview of all gamification systems: daily bonus, evoluti...

`GET /api/v1/stamps`

Description: list user's stamp collection

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/stamps" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/stamps/collections`

Description: list stamp collection sets grouped by trail

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/stamps/collections" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/stamps/daily-bonus`

Description: Returns the current daily bonus status including streak info, XP multiplier, and progress toward weekly/monthly stamps

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* DailyBonusResult */ }
}

Response data fields (DailyBonusResult):

FieldTypeDescription
xp_earnedinteger
xp_multipliernumber
bonus_xpinteger
is_first_of_dayboolean
consecutive_daysinteger
weekly_stamp_earnedboolean
weekly_stamp_namestring?
monthly_stamp_earnedboolean
monthly_stamp_namestring?

Example:

bash
curl -X GET "http://localhost:8080/api/v1/stamps/daily-bonus" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/stamps/evolution`

Description: Returns stamp evolution progress and earned evolved stamps. Tracks three evolution tiers: - Silver: 3 common stamps in same region - Gold: all stamps in a trail - Platinum: 3 complete trail systems

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* StampEvolutionResponse */ }
}

Response data fields (StampEvolutionResponse):

FieldTypeDescription
progress[]EvolutionProgressItem
earned[]EarnedEvolutionItem

Example:

bash
curl -X GET "http://localhost:8080/api/v1/stamps/evolution" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/stamps/encounters`

Description: Returns all random encounter stamps found by the user. Includes hidden stamps (5%), seasonal stamps (cherry blossom, etc.), and weather-based stamps (rain, snow, sunrise)

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/stamps/encounters" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/stamps/gamification`

Description: Returns a combined overview of all gamification systems: daily bonus, evolution progress, and random encounters

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { /* StampGamificationOverview */ }
}

Response data fields (StampGamificationOverview):

FieldTypeDescription
daily_bonusDailyBonusInfo
evolutionStampEvolutionResponse
random_encounters[]RandomEncounterResult
total_stampsinteger
total_evolutionsinteger
total_encountersinteger

Example:

bash
curl -X GET "http://localhost:8080/api/v1/stamps/gamification" \
  -H "Authorization: Bearer $TOKEN"

Checkpoints (체크포인트)

MethodPathAuthDescription
GET/api/v1/checkpoints/nearbyRequiredReturns checkpoints within the specified radius with stamp rarity, collection...
POST/api/v1/checkpoints/:id/qr-generateRequiredAdmin endpoint that generates a signed QR code for a checkpoint

`GET /api/v1/checkpoints/nearby`

Description: Returns checkpoints within the specified radius with stamp rarity, collection status, and random encounter info. This is the "Stamp Nearby!" feature (Pokemon GO PokeStop style)

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/checkpoints/nearby" \
  -H "Authorization: Bearer $TOKEN"

`POST /api/v1/checkpoints/:id/qr-generate`

Description: Admin endpoint that generates a signed QR code for a checkpoint

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
expires_atstring?No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/checkpoints/{id}/qr-generate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expires_at": "value"}'

Segments (구간)

MethodPathAuthDescription
GET/api/v1/segments/:id/leaderboardNoneReturns the top records (최고 기록) for a segment, ranked by fastest time
POST/api/v1/segments/:id/recordsRequiredSubmits a new timed record (구간 기록) for a segment

`GET /api/v1/segments/:id/leaderboard`

Description: Returns the top records (최고 기록) for a segment, ranked by fastest time

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/segments/{id}/leaderboard"

`POST /api/v1/segments/:id/records`

Description: Submits a new timed record (구간 기록) for a segment

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
duration_secondsintegerYesMin: 1
avg_speed_kmhnumber?No>= 0

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/segments/{id}/records" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"duration_seconds": 60, "avg_speed_kmh": 5.2}'

Feed (피드)

MethodPathAuthDescription
GET/api/v1/feedOptionalGet Feed
GET/api/v1/feed/followingRequiredGet Following Feed
GET/api/v1/feed/activityOptionalGet Activity Feed
GET/api/v1/feed/:idOptionalalias for getting a single post

`GET /api/v1/feed`

Description: Get Feed

Auth Required: Optional (enhances response with user context)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/feed"

`GET /api/v1/feed/following`

Description: Get Following Feed

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/feed/following" \
  -H "Authorization: Bearer $TOKEN"

`GET /api/v1/feed/activity`

Description: Get Activity Feed

Auth Required: Optional (enhances response with user context)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/feed/activity"

`GET /api/v1/feed/:id`

Description: alias for getting a single post

Auth Required: Optional (enhances response with user context)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/feed/{id}"

Posts (게시물)

MethodPathAuthDescription
POST/api/v1/postsRequiredCreate Post
POST/api/v1/posts/:id/likeRequiredLike Post
DELETE/api/v1/posts/:id/likeRequiredUnlike Post
POST/api/v1/posts/:id/commentsRequiredCreate Comment
GET/api/v1/posts/:id/commentsNoneGet Comments

`POST /api/v1/posts`

Description: Create Post

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
content_textstring?NoMax: 2000
activity_iduuid?No
trail_iduuid?No
photosstring[]NoMax: 10
visibilitystringNoOne of: public, followers, private
latitudenumber?NoValid latitude
longitudenumber?NoValid longitude
location_namestring?NoMax: 255

Response (201):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/posts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content_text": "Sample text content", "activity_id": "550e8400-e29b-41d4-a716-446655440000", "trail_id": "550e8400-e29b-41d4-a716-446655440000", "photos": ["item1", "item2"], "visibility": "public", "latitude": 37.5665, "longitude": 126.978, "location_name": "Sample Name"}'

`POST /api/v1/posts/:id/like`

Description: Like Post

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/posts/{id}/like" \
  -H "Authorization: Bearer $TOKEN"

`DELETE /api/v1/posts/:id/like`

Description: Unlike Post

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X DELETE "http://localhost:8080/api/v1/posts/{id}/like" \
  -H "Authorization: Bearer $TOKEN"

`POST /api/v1/posts/:id/comments`

Description: Create Comment

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
contentstringYesMin: 1; Max: 1000
parent_comment_iduuid?No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/posts/{id}/comments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Sample text content", "parent_comment_id": "550e8400-e29b-41d4-a716-446655440000"}'

`GET /api/v1/posts/:id/comments`

Description: Get Comments

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/posts/{id}/comments"

Kudos (응원)

MethodPathAuthDescription
POST/api/v1/kudosRequiredSend Kudos

`POST /api/v1/kudos`

Description: Send Kudos

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
checkin_iduuidYes
emojistringYesOne of: thumbs_up, fire, heart, clap, mountain, star
messagestring?NoMax: 200

Response (201):

json
{
  "success": true,
  "data": { /* KudosResponse */ }
}

Response data fields (KudosResponse):

FieldTypeDescription
iduuid
checkin_iduuid
emojistring
messagestring?
created_atdatetime

Example:

bash
curl -X POST "http://localhost:8080/api/v1/kudos" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"checkin_id": "550e8400-e29b-41d4-a716-446655440000", "emoji": "thumbs_up", "message": "value"}'

Events (이벤트)

MethodPathAuthDescription
POST/api/v1/eventsRequiredCreate Group Event
POST/api/v1/events/:id/rsvpRequiredUpdate RSVP

`POST /api/v1/events`

Description: Create Group Event

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
trail_iduuidYes
titlestringYesMin: 3; Max: 255
descriptionstring?NoMax: 2000
event_datestringYes
max_membersintegerYesMin: 2; Max: 100
is_publicbooleanNo

Response (201):

json
{
  "success": true,
  "data": { /* GroupEventResponse */ }
}

Response data fields (GroupEventResponse):

FieldTypeDescription
iduuid
organizer_iduuid
trail_iduuid
trail_namestring
titlestring
descriptionstring?
event_datedatetime
max_membersinteger
is_publicboolean
statusstring
going_countinteger
maybe_countinteger
user_rsvpstring?
created_atdatetime

Example:

bash
curl -X POST "http://localhost:8080/api/v1/events" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"trail_id": "550e8400-e29b-41d4-a716-446655440000", "title": "Sample Title", "description": "Sample text content", "event_date": "2026-03-30", "max_members": 20, "is_public": true}'

`POST /api/v1/events/:id/rsvp`

Description: Update RSVP

Auth Required: Yes (Bearer token)

Request Body (application/json):

FieldTypeRequiredValidation
statusstringYesOne of: going, maybe, not_going

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/events/{id}/rsvp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "going"}'

Notifications (알림)

MethodPathAuthDescription
GET/api/v1/notificationsRequiredreturns the current user's notifications
PATCH/api/v1/notifications/:id/readRequiredmarks a notification as read

`GET /api/v1/notifications`

Description: returns the current user's notifications

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/notifications" \
  -H "Authorization: Bearer $TOKEN"

`PATCH /api/v1/notifications/:id/read`

Description: marks a notification as read

Auth Required: Yes (Bearer token)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X PATCH "http://localhost:8080/api/v1/notifications/{id}/read" \
  -H "Authorization: Bearer $TOKEN"

Upload (업로드)

MethodPathAuthDescription
POST/api/v1/upload/photoRequiredupload a photo for check-in verification, social posts, or activities. Form ...
POST/api/v1/upload/avatarRequiredupload a profile avatar image. Form fields: - file: the image file (required...

`POST /api/v1/upload/photo`

Description: upload a photo for check-in verification, social posts, or activities. Form fields: - file: the image file (required, multipart) - category: one of "checkin", "social", "activity" (optional, defaults to "checkin") Returns the photo URL, thumbnail URL, filename, size, and MIME type

Auth Required: Yes (Bearer token)

Request (multipart/form-data):

FieldTypeRequiredDescription
filefileYesImage file (JPEG, PNG, HEIC, WebP)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/upload/photo" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg"

`POST /api/v1/upload/avatar`

Description: upload a profile avatar image. Form fields: - file: the image file (required, multipart, max 5MB) Avatars are stored in the "avatars" bucket which has public read access. Previous avatars are NOT deleted automatically (handled by a separate cleanup job)

Auth Required: Yes (Bearer token)

Request (multipart/form-data):

FieldTypeRequiredDescription
filefileYesImage file (JPEG, PNG, HEIC, WebP)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/upload/avatar" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg"

Nearby (주변)

MethodPathAuthDescription
GET/api/v1/nearby/amenitiesNoneQuery parameters: - lat: latitude (required) - lng: longitude (required) - ra...

`GET /api/v1/nearby/amenities`

Description: Query parameters: - lat: latitude (required) - lng: longitude (required) - radius: search radius in meters (optional, default 1000, max 5000) - type: amenity type filter (optional, e.g. "convenience_store", "restroom") - limit: maximum results (optional, default 20, max 50)

Auth Required: No

Query Parameters:

FieldTypeRequiredValidation
latnumberYesValid latitude
lngnumberYesValid longitude
radiusnumberNo> 0; Max: 5000
typestringNoOne of: convenience_store, restroom, water, rest_area, restaurant, cafe, parking, bus_stop
limitintegerNoMin: 1; Max: 50

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/nearby/amenities"

Weather (날씨)

MethodPathAuthDescription
GET/api/v1/weatherNoneReturns real weather data if an API key is configured, otherwise returns real...

`GET /api/v1/weather`

Description: Returns real weather data if an API key is configured, otherwise returns realistic mock data generated from the latitude, time of day, and season

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/weather"

Recommendations (추천)

MethodPathAuthDescription
GET/api/v1/recommendations/quick-startNoneQuery parameters: lat, lng, minutes (default 30), activity (default "walk") T...

`GET /api/v1/recommendations/quick-start`

Description: Query parameters: lat, lng, minutes (default 30), activity (default "walk") Tries the real recommendation service first. If no routes are found (e.g., DB is empty or no trails nearby), returns realistic mock data so the Flutter app always has content

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/recommendations/quick-start"

GPX

MethodPathAuthDescription
POST/api/v1/gpx/uploadRequiredUpload
POST/api/v1/gpx/parseNone(parse without saving)

`POST /api/v1/gpx/upload`

Description: Upload

Auth Required: Yes (Bearer token)

Request (multipart/form-data):

FieldTypeRequiredDescription
filefileYesImage file (JPEG, PNG, HEIC, WebP)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/gpx/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg"

`POST /api/v1/gpx/parse`

Description: (parse without saving)

Auth Required: No

Request (multipart/form-data):

FieldTypeRequiredDescription
filefileYesImage file (JPEG, PNG, HEIC, WebP)

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X POST "http://localhost:8080/api/v1/gpx/parse" \
  -F "file=@photo.jpg"

Knowledge Graph (지식 그래프)

MethodPathAuthDescription
GET/api/v1/graph/node/:idNoneReturns a node with all outgoing connections and backlinks (Obsidian-style)
GET/api/v1/graph/exploreNonePerforms multi-hop graph traversal from a center node
GET/api/v1/graph/searchNoneFull-text search across all knowledge nodes
GET/api/v1/graph/nearbyNoneFinds knowledge nodes near a geographic point using PostGIS
GET/api/v1/graph/statsNoneReturns summary statistics for the knowledge graph

`GET /api/v1/graph/node/:id`

Description: Returns a node with all outgoing connections and backlinks (Obsidian-style)

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/graph/node/{id}"

`GET /api/v1/graph/explore`

Description: Performs multi-hop graph traversal from a center node

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/graph/explore"

`GET /api/v1/graph/search`

Description: Full-text search across all knowledge nodes

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/graph/search"

`GET /api/v1/graph/nearby`

Description: Finds knowledge nodes near a geographic point using PostGIS

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/graph/nearby"

`GET /api/v1/graph/stats`

Description: Returns summary statistics for the knowledge graph

Auth Required: No

Response (200):

json
{
  "success": true,
  "data": { ... }
}

Example:

bash
curl -X GET "http://localhost:8080/api/v1/graph/stats"

Statistics

MetricCount
Total Endpoints96
GET Endpoints63
POST Endpoints28
PATCH Endpoints2
DELETE Endpoints3
Auth Required53
Auth Optional4
Public (No Auth)39
Endpoint Groups24

| Handler Documentation Coverage | 100% (94/94) | | DTOs Referenced | 39 | | Total DTOs Parsed | 98 |


*Generated by scripts/generate_api_docs.py on 2026-03-30 07:17 KST*