openapi: 3.1.0
info:
  title: Trepo Public API
  description: >
    Read-only public API for Trepo travel courses (trepo-trip.com).
    Use it to search and read public travel courses, community posts, profiles
    and group-trip events. Intended for blogs, research and AI agents
    (e.g. ChatGPT Actions). Fair use: attribute "Trepo (trepo-trip.com)",
    respect 429 rate limits, and do not redistribute bulk data to third parties.
  version: "1.0.0"
  contact:
    url: https://trepo-trip.com/developers.html
servers:
  - url: https://api.trepo-trip.com
paths:
  /api/courses/public:
    get:
      operationId: listPublicCourses
      summary: List public travel courses (paged)
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged list of public courses
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoursePage'
  /api/courses/popular:
    get:
      operationId: listPopularCourses
      summary: List popular courses (by stars)
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged list of popular courses
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoursePage'
  /api/courses/recent:
    get:
      operationId: listRecentCourses
      summary: List recently created courses
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged list of recent courses
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoursePage'
  /api/courses/search:
    get:
      operationId: searchCourses
      summary: Search courses by keyword (title, place name, address) and tags
      parameters:
        - name: keyword
          in: query
          required: false
          schema:
            type: string
          description: Matches course title, place names and addresses (e.g. a city name like "Seoul")
        - name: tags
          in: query
          required: false
          schema:
            type: string
          description: Comma-separated tag slugs
        - name: mode
          in: query
          required: false
          schema:
            type: string
            enum: [keyword, semantic]
            default: keyword
          description: semantic = embedding-based natural language search (falls back to keyword when disabled)
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoursePage'
  /api/courses/recommendations:
    get:
      operationId: listRecommendedCourses
      summary: Recommended courses (popularity-based for anonymous callers)
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged recommendations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoursePage'
  /api/courses/{id}:
    get:
      operationId: getCourse
      summary: Get course detail (day-by-day itinerary, places, tags, costs)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Course detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CourseDetail'
        '404':
          description: Not found or private
  /api/courses/{id}/similar:
    get:
      operationId: listSimilarCourses
      summary: Similar courses (tag overlap)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: List of similar courses
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Course'
  /api/posts/recent:
    get:
      operationId: listRecentPosts
      summary: Recent community posts
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged posts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PostPage'
  /api/posts/popular:
    get:
      operationId: listPopularPosts
      summary: Popular community posts
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Paged posts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PostPage'
  /api/users/{id}:
    get:
      operationId: getUserProfile
      summary: Public user profile (course author)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Public profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserProfile'
  /api/users/{id}/badges:
    get:
      operationId: getUserBadges
      summary: Public profile badges
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Badge list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    badgeType:
                      type: string
                    awardedAt:
                      type: string
                      format: date-time
  /api/events:
    get:
      operationId: listGroupTrips
      summary: Open group-trip recruitment events
      responses:
        '200':
          description: Open events (application handled on the organizer's external site)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TravelEvent'
  /api/events/course/{courseId}:
    get:
      operationId: listGroupTripsForCourse
      summary: Open group-trip events for a course
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Open events for the course
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TravelEvent'
components:
  schemas:
    Course:
      type: object
      description: Course summary. Share URL is https://trepo-trip.com/c/{id}
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
        authorUsername:
          type: string
        totalDays:
          type: integer
        starCount:
          type: integer
        forkCount:
          type: integer
        placeCount:
          type: integer
        tags:
          type: array
          items:
            type: object
      additionalProperties: true
    CourseDetail:
      allOf:
        - $ref: '#/components/schemas/Course'
        - type: object
          properties:
            places:
              type: array
              description: Ordered itinerary places (dayNumber = itinerary day)
              items:
                type: object
                properties:
                  order:
                    type: integer
                  dayNumber:
                    type: integer
                  notes:
                    type: string
                  place:
                    $ref: '#/components/schemas/Place'
          additionalProperties: true
    Place:
      type: object
      properties:
        name:
          type: string
        address:
          type: string
        latitude:
          type: number
        longitude:
          type: number
        category:
          type: string
      additionalProperties: true
    CoursePage:
      type: object
      properties:
        content:
          type: array
          items:
            $ref: '#/components/schemas/Course'
        totalElements:
          type: integer
        totalPages:
          type: integer
        number:
          type: integer
      additionalProperties: true
    Post:
      type: object
      properties:
        id:
          type: integer
        content:
          type: string
        authorUsername:
          type: string
        taggedCourseId:
          type: integer
        createdAt:
          type: string
          format: date-time
      additionalProperties: true
    PostPage:
      type: object
      properties:
        content:
          type: array
          items:
            $ref: '#/components/schemas/Post'
        totalElements:
          type: integer
      additionalProperties: true
    UserProfile:
      type: object
      properties:
        id:
          type: integer
        username:
          type: string
        followerCount:
          type: integer
        publicCourseCount:
          type: integer
        verificationBadge:
          type: string
      additionalProperties: true
    TravelEvent:
      type: object
      properties:
        id:
          type: integer
        courseId:
          type: integer
        courseTitle:
          type: string
        title:
          type: string
        organizerName:
          type: string
        departureDate:
          type: string
          format: date
        priceKrw:
          type: integer
        externalApplyUrl:
          type: string
          description: Application/payment is handled on the organizer's site
        status:
          type: string
      additionalProperties: true
