Skip to content

3. APIs and Controller

mgopal64 edited this page Dec 7, 2025 · 7 revisions

API

USER ENDPOINTS

Create User

/create_user allows new users to register by creating a user account with a username, password, and optional personal preferences. The password is securely hashed before storage, and a JWT access token is generated upon successful registration. Currently unused.

Endpoint: POST /create_user

Key Container Description
username JSON Unique username for the user (case-insensitive)
password JSON Plaintext password that will be hashed
fullname JSON User’s full name
likes JSON (Optional) User’s general travel likes/preferences
dislikes JSON (Optional) User’s general travel dislikes/preferences

Response: Returns a JSON object containing a success message, JWT access token, and user details.

Response Format:

{
    "message": "User created successfully",
    "access_token": access_token,
    "user": {
        "id": new_user.user_id,
        "username": new_user.username,
        "firstname": new_user.firstname,
        "lastname": new_user.lastname,
        "likes": new_user.likes,
        "dislikes": new_user.dislikes
    }
}

Login User

/login authenticates a user by verifying their username and password. Upon successful authentication, it generates a new JWT token for authorization in future requests. Used when logging in a user either through the automatic demo flow or via manual login.

Endpoint: POST /login

Key Container Description
username JSON Registered username of the user
password JSON Password associated with the username

Response: Returns a JSON object with the login success message, JWT token, and basic user info.

Response Format:

{
    "message": "Login successful",
    "access_token": access_token,
    "user": {
        "id": user.user_id,
        "username": user.username,
        "firstname": user.firstname,
        "lastname": user.lastname,
        "likes": user.likes,
        "dislikes": user.dislikes
    }
}

Get User

/user/<int:user_id> obtains a user record by their user_id. If the user doesn't exist it errors, otherwise it returns the user as a serialized JSON object. Used when logging in a user either through the automatic demo flow or via manual login.

Endpoint: POST /user/<int:user_id>

Key Container Description
user_id JSON The id of the user to retrieve

Response: Returns a JSON object of the user info.

Response Format:

{
    "user_id": user.user_id,
    "username": user.username,
    "email": user.email,
    "firstname": user.firstname,
    "lastname": user.lastname,
    "likes": user.likes,
    "dislikes": user.dislikes
}

Edit User Info

/edit_user allows authenticated users to update specific personal information fields such as username, first name, last name, likes, and dislikes. Used when updating user data.

Endpoint: POST /edit_user

Authorization: Requires valid JWT token in header

Authorization: Bearer <access_token>

Key Container Description
username JSON User’s username
firstname JSON User’s first name
lastname JSON User’s last name
likes JSON Updated user travel likes/preferences
dislikes JSON Updated user travel dislikes/preferences

Response: Returns updated user information and confirmation message.

Response Format:

{
    "message": "User updated successfully",
    "user": {
        "id": user.user_id,
        "username": user.username,
        "firstname": user.firstname,
        "lastname": user.lastname,
        "likes": user.likes,
        "dislikes": user.dislikes
    }
}

FRIENDSHIP ENDPOINTS

Get Friends

/get_friends/<user_id> is called to retrieves a list of user IDs for all confirmed friends of the specified user. Friends are detected using the IsFriends relation between the queried user_id and all other users and checking that relationship is True. This builds the friends list as a list of user_ids which are serialized into a JSON array which allows the front end to generate the list of friends from each user_id.

Endpoint: GET /get_friends/<user_id>

Authorization: Requires JWT token

Response: JSON Array of user IDs

Request Parameters:

Key Container Type Description
user_id URL int The unique identifier of the user whose friends to retrieve

Example Response:

["user123", "user456", "user789"]

Get Invites

/get_invites/<user_id> is called to fetch all incoming friend requests for the specified user. Incoming friends requests are determined by using the IsFriends relation and checking that relationship is False and the queried user is not initiating a friendship request. This builds a list of user_ids of people with incoming friendship requests to the queried user and serializes them into a JSON array which the front end can use to generate a list of users sending friend requests.

Endpoint: GET /get_invites/<user_id>

Response: JSON Array of user IDs

Request Parameters:

Key Container Type Description
user_id URL int The unique identifier of the user whose invites to retrieve

Example Response:

["user123", "user456", "user789"]

Get Relationship

/get_relationship/<user_id1>/<user_id2> is called to fetch the relationship status between the two queried users. Relationships are determined using the IsFriends relation, where absence of IsFriends indicates "none", existence with relationship being True indicates "friends", and existence with relationship being False indicating a "pending incoming/outgoing" relationship depending on the initiator_id. This endpoint returns this status as a JSON serialized string which the front end can use to determine how two users are related.

Endpoint: GET /get_relationship/<user_id1>/<user_id2>

Response: JSON of status dict between users

Request Parameters:

Key Container Type Description
user_id1 URL int The first user's unique identifier
user_id2 URL int The second user's unique identifier

Example Response:

{"status": "friends"}

Send Friend Request

/send_friend_request/<user_id> allows the authenticated user to send a friend request to another user. This endpoint validates that the queried user_id exists and doesn't match the current user via their JWT token, then validates that no current relationship exists, and then creates the request via a "pending outgoing" relation in the IsFriends relation. This endpoint returns a simple success message via JSON.

Endpoint: POST /send_friend_request/<user_id>

Authorization: Requires JWT token

Key Container Description
user_id JSON The user ID of the person to send the friend request to.

Response: Returns confirmation that friend request was sent.

/accept_friend_request/<int:user_id> allows the authenticated user to accept a friend request from the queried user. This endpoint validates that both users exist and that there is a "pending incoming" relation in the IsFriends relation to the current user via their JWT. It then updates the relationship to True. This endpoint returns a simple success message via JSON.

Accept Friend Request

Accepts a pending friend request from another user.

Endpoint: POST /accept_friend_request/<int:user_id>

Authorization: Requires JWT token

Key Container Description
user_id JSON The user ID of the person whose request is being accepted

Response: Returns confirmation that the friend request was accepted and relationship updated.

Decline Friend Request

/decline_friend_request/<int:user_id> allows the authenticated user to decline a friend request from the queried user. This endpoint validates that both users exist and that there is a "pending incoming" relation in the IsFriends relation to the current user via their JWT. It then deletes the IsFriends relation. This endpoint returns a simple success message via JSON.

Endpoint: POST /decline_friend_request/<int:user_id>

Authorization: Requires JWT token

Key Container Description
user_id JSON The user ID of the person whose request is being declined

Response: Returns confirmation message that the request was declined.

Revoke Friend Request

/revoke_friend_request/<user_id> allows the authenticated user to revoke a friend request sent to the queried user. This endpoint validates that both users exist and that there is a "pending outgoing" relation in the IsFriends relation from the current user via their JWT. It then deletes the IsFriends relation. This endpoint returns a simple success message via JSON.

Endpoint: POST /revoke_friend_request/<user_id>

Authorization: JWT Token Required

Response:

{"error": "No pending friend request found"}

Remove Friend

/remove_friend/<int:user_id> allows the authenticated user to remove the queried user as a friend. This endpoint validates that both users exist and that there is a "friend" relation in the IsFriends between the queried and current user. It then deletes the IsFriends relation. This endpoint returns a simple success message via JSON.

Endpoint: /remove_friend/<int:user_id>

Authorization: JWT Token Required

Response:

{"message": "Friend removed successfully"}

Search for Users, for Add Friends feature

/search_friends performs a live/debounced username search to find other users by username. It then sorts these friends and assigns their status based on relationship before sending them to the frontend as a JSON Array of user_ids. Used to enable user search.

Endpoint: GET /search_friends

Authorization: Requires JWT token

Response: Returns a JSON array of user_ids which match the query.

Response Format:

[
         {
            "user_id": user.user_id,
            "username": user.username,
            "firstname": user.firstname,
            "lastname": user.lastname,
            "status": status
         },
         {
            "user_id": user.user_id,
            "username": user.username,
            "firstname": user.firstname,
            "lastname": user.lastname,
            "status": status
          },
         ...
]

TRIP ENDPOINTS

Submit Form

/submit_form allows the user to generate 3 AI-powered trip itineraries using Gemini LLM based on destination, trip-specific preferences, and user-level preferences. The LLM returns structured JSON trip data containing stops and activities, and then chains a second call to Gemini LLM to obtain trip-wide metrics for cost and transportation. Used to generate itinerary choices.

Endpoint: POST /submit_form

Authorization: Requires JWT token

Key Container Description
destination JSON Primary destination for the trip
num_versions JSON Number of itinerary versions to generate (3)
numDays JSON Number of days to schedule itinerary for
stops JSON String describing which stops the user is interested in
timeline JSON String describing what kind of timeline they'd like on their trip

Response: Returns structured AI-generated trip itineraries following the strict JSON schema.

Response Format:

{
    "trips": [
        {
        "version": 1,
        "name": "string",
        "description": "string",
        "stops": [
            {
            "name": "string",
            "latitude": number,
            "longitude": number,
            "stop_type": "string",
            "order": number,
            },
            ...
        ],
        "total_cost_estimate" : number,
        "cost_breakdown" : "string",
        "transportation_summary" : "string",
        "transportation_breakdown" : "string"
        },
        ...
    ]
}

Get Active Trips

/get_active_trips/<int:user_id> gets the list of trips that the queried user is actively on by filtering for matching owner and not completed status. Used for listing a user's active trips.

Endpoint: GET /get_active_trips/<int:user_id>

Key Container Description
user_id URL The id of the user to retrieve active trips for

Response: Returns a JSON array of active trip IDs

Get Completed Trips

/get_completed_trips/<int:user_id> gets the list of trips that the queried user has archived on by filtering for matching owner and completed status. Used for listing a user's completed trips.

Endpoint: GET /get_completed_trips/<int:user_id>

Key Container Description
user_id URL The id of the user to retrieve completed trips for

Response: Returns a JSON array of completed trip IDs

Get Friends Trips

/get_friends_trips/<int:user_id> gets the list of trips that the queried user has been invited on by friends by filtering for trips with a PartOf relation and not matching owner. Used for listing a user's friend's trips.

Endpoint: GET /get_friends_trips/<int:user_id>

Key Container Description
user_id URL The id of the user to retrieve friend's trips for

Response: Returns a JSON array of friend's trip IDs

Get Stop

/stop/<int:stop_id> gets the stop by queried stop_id as a serialized JSON object. Used for getting a particular stop.

Endpoint: GET /stop/<int:stop_id>

Key Container Description
stop_id URL The id of the stop to retrieve

Response: Returns a JSON object of a stop.

Get Trip

/trip/<int:trip_id> returns information stored within the DB record for the specified trip_id as a serialized JSON object. Used for getting a particular trip.

Endpoint: GET /trip/<int:trip_id>

Key Type Description
trip_id URL The unique identifier of the trip

Response Example:

{
    "trip_id": trip.trip_id,
    "owner_id": trip.owner_id,
    "status": str(trip.status),
    "name": trip.name,
    "description": trip.description,
    "cost_breakdown": trip.cost_breakdown,
    "total_cost_estimate": trip.total_cost_estimate,
    "transportation_breakdown": trip.transportation_breakdown,
    "transportation_summary": trip.transportation_summary,
    "driving_polyline": str(trip.driving_polyline),
    "driving_polyline_timestamp": trip.driving_polyline_timestamp,
    "stop_ids": [stop.stop_id for stop in sorted_stops],
    "invited_friends": [u.user_id for u in trip.participants if u.user_id != trip.owner_id]
}

Update Stop Completed

/update_stop_completed/<int:stop_id> marks the queried stop as completed. Used for checking off stops.

Endpoint: PUT /update_stop_completed/<int:stop_id>

Key Type Description
stop_id URL The unique identifier of the stop

Response Example:

{
    "message": "Stop {stop_id} updated successfully!"
}

Get Trip Stops

/trip/<int:trip_id>/stops returns the stops associated with the specified trip_id as a JSON array of serialized stops. Used for getting an itinerary list.

Endpoint: GET /trip/<int:trip_id>/stops

Key Type Description
trip_id URL The unique identifier of the trip

Response Example:

[
    {
        "stop_id": s.stop_id,
        "stop_type": s.stop_type,
        "latitude": s.latitude,
        "longitude": s.longitude,
        "name": s.name,
        "completed": s.completed,
        "order": s.order,
        "address": s.address,
        "hours": s.hours,
        "rating": s.rating,
        "priceRange": s.priceRange,
        "googleMapsUri": s.googleMapsUri
    }
    ...
]

Add Stop

/add_stop creates a stop in the backend and returns it to the front end via a JSON dictionary of a stop_id to update the front end and add the stop to the trip with a state update. Used for adding a stop to a trip.

Endpoint: POST /add_stop

Key Type Description
name JSON name to give the stop
latitude JSON the latitude of the stop
longitude JSON the longitude of the stop

Response Example:

{
    "stop_id": new_stop.stop_id
}

Modify Itinerary

/trip/<int:trip_id>/stops batches itinerary modifications after several edits from a user by capturing the final state of stop_id's after edits and using it to update order of, create new, and delete old stops. Used for batching edits with the confirm changes button in edit itinerary.

Endpoint: PATCH /trip/<int:trip_id>/stops

Key Type Description
trip_id URL The unique identifier of the trip
stopIds JSON The resultant list of stop ids to edit the itinerary with

Response Example:

{
    "stopIds": stop_ids
}

Choose Trip

/choose_trip forwards the selected trip suggestion to the backend for trip creation. Additionally creates place information for each stop and regenerates driving polyline data for the trip. Used for making a trip after being selected.

Endpoint: POST /choose_trip

Authorization: Requires JWT token

Key Type Description
name name of new trip
description description of new trip
cost_breakdown cost breakdown of trip as a string
total_cost_estimate cost estimate as a number
transportation_breakdown transportation breakdown string
transportation_summary transportation summary as a string
stops JSON JSON array of serialized stop objects missing stop_id's and trip_id's

Response Example:

{
    "message": "Trip saved successfully",
    "trip_id": new_trip.id,
    "trip": {
        "trip_id": new_trip.trip_id,
        "owner_id": new_trip.owner_id,
        "name": new_trip.name,
        "description": new_trip.description,
        "cost_breakdown": new_trip.cost_breakdown,
        "total_cost_estimate": new_trip.total_cost_estimate,
        "transportation_breakdown": new_trip.transportation_breakdown,
        "transportation_summary": new_trip.transportation_summary,
        "status": new_trip.status,
        "stop_count": len(stops_data)
    }
}

Get Trip

/trips/<int:trip_id>/archive updates the specified trip upject with status completed once the user has finished. Used for archiving finished trips.

Endpoint: PATCH /trips/<int:trip_id>/archive

Key Type Description
trip_id URL The unique identifier of the trip

Response Example:

{
    "message": "Trip {trip_id} archived successfully!"
}

LLM Chat (streamed via SSE)

/trips/<int:trip_id>/chat_sse This endpoint allows the user to send chats to the Gemini LLM to ask questions about the selected trip. LLM responses are streamed back using SSE, with the endpoint using an accumulator generator pattern to stream SSE to the client while updating the database. Used for dynamically learning about trip with chatbot.

Endpoint: POST /trips/<int:trip_id>/chat_sse

Authorization: Requires JWT token

Key Container Description
message JSON User text for their message
trip_id URL the unique identifier for this trip
longitude multipart/data A double representing current longitude

Response: Returns a mime/event_stream generator which gets fed via the /streamGenerateContent endpoint from Gemini and is exhausted on the client side to render LLM response in real time and quickly.

CAMERA ENDPOINTS

Landmark Context

/landmark_context This endpoint allows the user to obtain context based on an image they take with their camera on the app intended for helping travelers identify and learn more about attractions and landmarks they encounter. Used for identifying landmarks using location information to provide context.

Endpoint: POST /landmark_context

Authorization: Optional JWT token (Demo Flow)

Key Container Description
image multipart/form An image file taken by the user of a landmark they want context for.
latitude multipart/data A double representing current latitude
longitude multipart/data A double representing current longitude

Response: Returns a JSON object containing LLM response test describing the image's context, location, and self reported confidence level.

Response Format:

{
        "context": text_response
}

Expected 201 Status Code on success.

PARTY MANAGEMENT

Invite Friend to Trip

/invite_friend/<int:trip_id>/<int:user_id> allows the trip owner to invite a confirmed friend to join their trip. Ownership is verified through the JWT token — the user making the request must be the trip’s owner. Only confirmed friends can be invited. Used to invite friends to trips.

Endpoint: POST /invite_friend/<int:trip_id>/<int:user_id>

Authorization: Requires JWT token

Key Container Description
user_id URL The ID of the friend to invite
trip_id URL The trip ID for which the invitation is being sent

Response Example:

{
  "message": "User {user_id} invited to trip {trip_id}"
}

Uninvite Friend From Trip

/uninvite_friend/<int:trip_id>/<int:user_id> allows the trip owner to uninvite a friend from their trip. Ownership is verified through the JWT token — the user making the request must be the trip’s owner. Only confirmed friends can be invited. Used to uninvite friends from trips.

Endpoint: POST /uninvite_friend/<int:trip_id>/<int:user_id>

Authorization: Requires JWT token

Key Container Description
user_id URL The ID of the friend to invite
trip_id URL The trip ID for which the invitation is being sent

Response Example:

{
  "message": "User {user_id} uninvited from trip {trip_id} successfully!"
}

Third-Party APIs

LLM API using Gemini Flash 2.5

Gemini's LLM API is used in 3 primary ways throughout TripView: Reading text to generate itineraries, identifying images to generate context, and performing chatbot duties. Here are the following endpoints:

Text Parsing

Endpoint: POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$GEMINI_API_KEY

Request format:

{
    "contents": [{
        "parts":[{"text": "Generate an itinerary..."}],
        ...
    }]
}

Response: Instance of GenerateContentResponse

Response Format:

{
  "candidates": [
    {
      object ([Candidate](https://ai.google.dev/api/generate-content#v1beta.Candidate))
    }
  ],
  "promptFeedback": {
    object ([PromptFeedback](https://ai.google.dev/api/generate-content#PromptFeedback))
  },
  "usageMetadata": {
    object ([UsageMetadata](https://ai.google.dev/api/generate-content#UsageMetadata))
  },
  "modelVersion": string,
  "responseId": string
}

Image Recognition and Analysis

Endpoint: POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$GEMINI_API_KEY

Request format:

{
  "contents": [{
    "parts":[
      {"text": "Tell me about this landmark located here..."},
      {
        "inline_data": {
          "mime_type":"image/jpeg",
          "data": b64_encoding_of_image
        }
      }
    ]
  }]
}

Response: Instance of GenerateContentResponse

Response Format:

{
  "candidates": [
    {
      object ([Candidate](https://ai.google.dev/api/generate-content#v1beta.Candidate))
    }
  ],
  "promptFeedback": {
    object ([PromptFeedback](https://ai.google.dev/api/generate-content#PromptFeedback))
  },
  "usageMetadata": {
    object ([UsageMetadata](https://ai.google.dev/api/generate-content#UsageMetadata))
  },
  "modelVersion": string,
  "responseId": string
}

Chatbot with streamed SSE messages

Endpoint: POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?key=$GEMINI_API_KEY

Request format:

{
  "contents": [
    {"role":"user",
     "parts":[{
       "text": "What is my trip?"}]},
    {"role": "model",
     "parts":[{
       "text": "Your trip is to Ann Arbor?"}]},
    {"role":"user",
     "parts":[{
       "text": "What did I ask you?"}]},
    {"role": "model",
     "parts":[{
       "text": "You asked 'What is my trip?'}]},
  ]
}

Response: A stream of GenerateContentResponse instances

Response Format for one instance of response stream:

{
  "candidates": [
    {
      object ([Candidate](https://ai.google.dev/api/generate-content#v1beta.Candidate))
    }
  ],
  "promptFeedback": {
    object ([PromptFeedback](https://ai.google.dev/api/generate-content#PromptFeedback))
  },
  "usageMetadata": {
    object ([UsageMetadata](https://ai.google.dev/api/generate-content#UsageMetadata))
  },
  "modelVersion": string,
  "responseId": string
}

OSRM Routing API

https://project-osrm.org/docs/v5.5.1/api/#route-service (used here) This endpoint allows us to receive a driving polyline between stops on a route. Polylines come in Google encoded format and are then decoded into LatLng pairs in the frontend.

Endpoint: GET https://routing.openstreetmap.de/routed-car/route/v1/driving/{coordinatesSubStr}?geometries=polyline6 where {coordinatesSubStr} is a semicolon-seperated list of longitude and latitude tuples. For example: {coordinatesSubStr} = -83.7269,42.2986;-83.7753,42.2825

Example request: https://routing.openstreetmap.de/routed-car/route/v1/driving/-83.7269,42.2986;-83.7753,42.2825?geometries=polyline6

Example response:

{
    "code": "Ok",
    "routes": [{
        "legs": [{
            "steps": [],
            "weight": 489.1,
            "summary": "",
            "duration": 489.1,
            "distance": 5072.5
        }],
        "weight_name": "routability",
        "geometry": "squtoAjhhu~CltIbbOr{CpbDpiAjo@tiCphGbtCtrI`xEpJya@xl`@unBrlO`[bL|l@taA",
        "weight": 489.1,
        "duration": 489.1,
        "distance": 5072.5
    }],
    "waypoints": [{
        "hint": "Yw_DhEJfpIYUAAAABAAAAHYAAAAIAQAAyY_MQbJpekAFiRRDnKSlQxQAAAAEAAAAdgAAAAgBAADJdQAAam0C-ypthQLMbQL76GyFAhYA7w0AAAAA",
        "location": [-83.726998, 42.298666],
        "name": "Plymouth Road",
        "distance": 10.91341369
    }, {
        "hint": "6YdkgC7YZIA7AAAAFwAAABEBAAAIAAAAVRYlQmNBfEG2wD1DHo-7QDsAAAAXAAAAEQEAAAgAAADJdQAAuLAB-1ouhQK8sAH7BC6FAgoAfwUAAAAA",
        "location": [-83.775304, 42.282586],
        "name": "Longman Lane",
        "distance": 9.558459137
    }]
}

From this response, we take routes[0]["geometry"] and store in trip database object to later be sent to the frontend whenever changes are made to the stops on that trip.

Google Places API Nearby Search (New)

https://developers.google.com/maps/documentation/places/web-service/nearby-search (used here) This endpoint allows us to receive information about a stop such as ratings, hours, price range, address, and a link to that stops Google Maps entry. The endpoint takes a latitude and longitude and searches in a circle of radius 50 meters and returns information about the top result within that circle. This endpoint is called whenever a stop is newly created, either in trip creation or later on.

Endpoint: GET https://places.googleapis.com/v1/places:searchNearby

Headers:

{
    "X-Goog-Api-Key": [MAPS_API_KEY as set in .env],
    "X-Goog-FieldMask": "places.rating,places.priceRange,places.currentOpeningHours,places.shortFormattedAddress,places.googleMapsUri",
    "Content-Type": "application/json; charset=UTF-8"
}

Request Data:

{
    "maxResultCount": 1,
    "locationRestriction": {
        "circle": {
            "center": {
                "latitude": [Stop latitude from DB],
                "longitude": [Stop longitude from DB],
            },
            "radius": 50.0
        }
    }
}

Example response:

"places": [
      {
        "currentOpeningHours": {
          "nextOpenTime": "2025-11-24T14:00:00Z",
          "openNow": false,
          "periods": [
            {}, // Not displayed for brevity
            ...
          ],
          "weekdayDescriptions": [
            "Monday: 9:00\u202fAM\u2009\u2013\u20094:00\u202fPM",
            ...
          ]
        },
        "priceRange": {
          "endPrice": {
            "currencyCode": "USD",
            "units": "20"
          },
          "startPrice": {
            "currencyCode": "USD",
            "units": "10"
          }
        },
        "googleMapsUri": "https://maps.google.com/?cid=15450195039452542620&g_mp=Cilnb29nbGUubWFwcy5wbGFjZXMudjEuUGxhY2VzLlNlYXJjaE5lYXJieRACGAQgAA",
        "rating": 4.4,
        "shortFormattedAddress": "2781 Packard St, Ann Arbor"
      }
    ]
  }

From this response, we take the following data and adapt it/store in the following attributes on the stop database object:

JSON Response Key Attribute in DB
["places"][0]["rating"] rating
["places"][0]["priceRange"]["startPrice"]["units"] priceRange
["places"][0]["priceRange"]["endPrice"]["units"] priceRange
["places"][0]["shortFormattedAddress"] address
All items of ["places"][0]["currentOpeningHours"]["weekdayDescriptions"] hours
["places"][0]["googleMapsUri"] googleMapsUri