{ "swagger": "2.0", "info": { "description": "Bringing group travel plans to life", "title": "Toggo API", "contact": {}, "version": "1.0" }, "host": "localhost:8000", "basePath": "/", "paths": { "/api/v1/comments": { "post": { "description": "Creates a new comment", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "comments" ], "summary": "Create a comment", "operationId": "createComment", "parameters": [ { "description": "Create comment request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.CreateCommentRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.Comment" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/comments/{commentID}": { "delete": { "description": "Deletes a comment by ID", "tags": [ "comments" ], "summary": "Delete a comment", "operationId": "deleteComment", "parameters": [ { "type": "string", "description": "Comment ID", "name": "commentID", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "patch": { "description": "Updates an existing comment by ID", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "comments" ], "summary": "Update a comment", "operationId": "updateComment", "parameters": [ { "type": "string", "description": "Comment ID", "name": "commentID", "in": "path", "required": true }, { "description": "Update comment request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UpdateCommentRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.Comment" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/files/confirm": { "post": { "description": "Verifies the file exists in S3 and marks the upload as confirmed in DB", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "files" ], "summary": "Confirm upload", "operationId": "confirmUpload", "parameters": [ { "description": "Confirm upload request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.ConfirmUploadRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.ConfirmUploadResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/files/health": { "get": { "description": "Verifies connectivity to the configured S3 bucket", "produces": [ "application/json" ], "tags": [ "files" ], "summary": "Check S3 connection", "operationId": "checkS3Health", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.S3HealthCheckResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/files/upload": { "post": { "description": "Generates presigned PUT URLs for uploading files and creates pending records in DB", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "files" ], "summary": "Create upload URLs", "operationId": "createUploadURLs", "parameters": [ { "description": "Upload URL request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UploadURLRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.UploadURLResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/files/{imageId}": { "get": { "description": "Retrieves presigned URLs for all sizes of an image", "produces": [ "application/json" ], "tags": [ "files" ], "summary": "Get all sizes of a file", "operationId": "getFileAllSizes", "parameters": [ { "type": "string", "description": "Image ID (UUID)", "name": "imageId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.GetFileAllSizesResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/files/{imageId}/{size}": { "get": { "description": "Retrieves a presigned URL for downloading a specific file size", "produces": [ "application/json" ], "tags": [ "files" ], "summary": "Get file by ID and size", "operationId": "getFile", "parameters": [ { "type": "string", "description": "Image ID (UUID)", "name": "imageId", "in": "path", "required": true }, { "type": "string", "description": "Image size (large, medium, small)", "name": "size", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.GetFileResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/memberships": { "post": { "description": "Adds a user as a member of a trip", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "memberships" ], "summary": "Add member to trip", "operationId": "addMember", "parameters": [ { "description": "Create membership request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.CreateMembershipRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.Membership" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/notifications/send": { "post": { "description": "Sends a push notification to a single user", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "notifications" ], "summary": "Send notification to user", "operationId": "sendNotification", "parameters": [ { "description": "Notification request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.SendNotificationRequest" } } ], "responses": { "200": { "description": "OK" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/notifications/send-bulk": { "post": { "description": "Sends push notifications to multiple users", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "notifications" ], "summary": "Send bulk notifications", "operationId": "sendBulkNotification", "parameters": [ { "description": "Bulk notification request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.SendBulkNotificationRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NotificationResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/search/places/details": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Retrieves detailed information about a specific place using Google Maps Places API. Accepts either a place_id (from typeahead results) or input text for direct search. Returns address, coordinates, photos, ratings, opening hours, and more.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "places" ], "summary": "Get detailed information about a place", "operationId": "getPlaceDetails", "parameters": [ { "description": "Place details request (provide either place_id or input)", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.PlaceDetailsRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.PlaceDetailsResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/search/places/health": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Checks if Google Maps API is connected and accessible", "produces": [ "application/json" ], "tags": [ "places" ], "summary": "Google Maps health check", "operationId": "googleMapsHealth", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/api/v1/search/places/typeahead": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Quick typeahead/autocomplete endpoint for real-time place search as users type. Returns a list of place predictions with place IDs that can be used to fetch detailed information.", "produces": [ "application/json" ], "tags": [ "places" ], "summary": "Typeahead search for places", "operationId": "typeaheadPlaces", "parameters": [ { "type": "string", "description": "Search query (e.g., 'Eiffel Tower', 'Paris')", "name": "q", "in": "query", "required": true }, { "type": "integer", "description": "Maximum number of results (default: 5, max: 20)", "name": "limit", "in": "query" }, { "type": "string", "description": "Language code (e.g., 'en', 'fr', 'es')", "name": "language", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.PlacesSearchResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips": { "get": { "description": "Retrieves trips with cursor-based pagination. Use limit and cursor query params.", "produces": [ "application/json" ], "tags": [ "trips" ], "summary": "Get all trips", "operationId": "getAllTrips", "parameters": [ { "type": "integer", "description": "Max items per page (default 20, max 100)", "name": "limit", "in": "query" }, { "type": "string", "description": "Opaque cursor from previous response next_cursor for next page", "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.TripCursorPageResult" } }, "400": { "description": "Invalid cursor", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "post": { "description": "Creates a new trip for the authenticated user", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "trips" ], "summary": "Create a trip", "operationId": "createTrip", "parameters": [ { "description": "Create trip request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.CreateTripRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.Trip" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}": { "get": { "description": "Retrieves a trip by ID", "produces": [ "application/json" ], "tags": [ "trips" ], "summary": "Get a trip", "operationId": "getTrip", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.TripAPIResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "delete": { "description": "Deletes a trip by ID", "tags": [ "trips" ], "summary": "Delete a trip", "operationId": "deleteTrip", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "patch": { "description": "Updates an existing trip by ID", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "trips" ], "summary": "Update a trip", "operationId": "updateTrip", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "description": "Update trip request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UpdateTripRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.Trip" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}/invites": { "post": { "description": "Creates a shareable invite for the trip. Caller must be a trip member.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "trips" ], "summary": "Create a trip invite", "operationId": "createTripInvite", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "description": "Optional expires_at; default 7 days", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.CreateTripInviteRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.TripInviteAPIResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}/memberships": { "get": { "description": "Retrieves all members of a trip", "produces": [ "application/json" ], "tags": [ "memberships" ], "summary": "Get trip members", "operationId": "getTripMembers", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "integer", "description": "Max items per page (default 20, max 100)", "name": "limit", "in": "query" }, { "type": "string", "description": "Opaque cursor returned in next_cursor", "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.MembershipCursorPageResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}/memberships/{userID}": { "get": { "description": "Retrieves the membership for a user in a trip", "produces": [ "application/json" ], "tags": [ "memberships" ], "summary": "Get membership", "operationId": "getMembership", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.MembershipAPIResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "delete": { "description": "Removes a user from a trip", "tags": [ "memberships" ], "summary": "Remove member from trip", "operationId": "removeMember", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "patch": { "description": "Updates a membership", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "memberships" ], "summary": "Update membership", "operationId": "updateMembership", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true }, { "description": "Update membership request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UpdateMembershipRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.Membership" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}/memberships/{userID}/demote": { "post": { "description": "Demotes an admin to regular member role", "tags": [ "memberships" ], "summary": "Demote admin to member", "operationId": "demoteFromAdmin", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}/memberships/{userID}/promote": { "post": { "description": "Promotes a member to admin role", "tags": [ "memberships" ], "summary": "Promote member to admin", "operationId": "promoteToAdmin", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/trips/{tripID}/{entityType}/{entityID}/comments": { "get": { "description": "Retrieves paginated comments for a trip entity", "produces": [ "application/json" ], "tags": [ "comments" ], "summary": "Get comments", "operationId": "getPaginatedComments", "parameters": [ { "type": "string", "description": "Trip ID", "name": "tripID", "in": "path", "required": true }, { "type": "string", "description": "Entity type (activity, pitch)", "name": "entityType", "in": "path", "required": true }, { "type": "string", "description": "Entity ID", "name": "entityID", "in": "path", "required": true }, { "type": "integer", "description": "Max items per page (default 20, max 100)", "name": "limit", "in": "query" }, { "type": "string", "description": "Opaque cursor returned in next_cursor", "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.PaginatedCommentsResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/users": { "post": { "description": "Creates a new user with the provided payload", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "users" ], "summary": "Create a new user", "operationId": "createUser", "parameters": [ { "description": "Create user request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.CreateUserRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.User" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/users/me": { "get": { "description": "Retrieves the authenticated user (from JWT claims)", "produces": [ "application/json" ], "tags": [ "users" ], "summary": "Get current user", "operationId": "getCurrentUser", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.User" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/api/v1/users/{userID}": { "get": { "description": "Retrieves a user by ID", "produces": [ "application/json" ], "tags": [ "users" ], "summary": "Get a user", "operationId": "getUser", "parameters": [ { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.User" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "delete": { "description": "Deletes a user by ID", "tags": [ "users" ], "summary": "Delete a user", "operationId": "deleteUser", "parameters": [ { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } }, "patch": { "description": "Updates an existing user by ID", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "users" ], "summary": "Update a user", "operationId": "updateUser", "parameters": [ { "type": "string", "description": "User ID", "name": "userID", "in": "path", "required": true }, { "description": "Update user request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UpdateUserRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.User" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/errs.APIError" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/errs.APIError" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/errs.APIError" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/errs.APIError" } } } } }, "/healthcheck": { "get": { "description": "Returns OK if the server is running and database is healthy", "produces": [ "application/json" ], "tags": [ "example" ], "summary": "Healthcheck endpoint", "operationId": "healthcheck", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } } }, "definitions": { "errs.APIError": { "type": "object", "properties": { "message": {}, "statusCode": { "type": "integer" } } }, "models.AddressComponent": { "type": "object", "properties": { "long_name": { "type": "string" }, "short_name": { "type": "string" }, "types": { "type": "array", "items": { "type": "string" } } } }, "models.Bounds": { "type": "object", "properties": { "northeast": { "$ref": "#/definitions/models.LatLng" }, "southwest": { "$ref": "#/definitions/models.LatLng" } } }, "models.Comment": { "type": "object", "properties": { "content": { "type": "string" }, "created_at": { "type": "string" }, "entity_id": { "type": "string" }, "entity_type": { "$ref": "#/definitions/models.EntityType" }, "id": { "type": "string" }, "trip_id": { "type": "string" }, "updated_at": { "type": "string" }, "user_id": { "type": "string" } } }, "models.CommentAPIResponse": { "type": "object", "properties": { "content": { "type": "string" }, "created_at": { "type": "string" }, "entity_id": { "type": "string" }, "entity_type": { "$ref": "#/definitions/models.EntityType" }, "id": { "type": "string" }, "profile_picture_url": { "description": "pointer since some users don't have their avatar set", "type": "string" }, "trip_id": { "type": "string" }, "updated_at": { "type": "string" }, "user_id": { "type": "string" }, "username": { "type": "string" } } }, "models.ConfirmUploadRequest": { "type": "object", "required": [ "imageId" ], "properties": { "imageId": { "type": "string" }, "size": { "description": "Optional: if nil, confirm all sizes", "allOf": [ { "$ref": "#/definitions/models.ImageSize" } ] } } }, "models.ConfirmUploadResponse": { "type": "object", "properties": { "confirmed": { "type": "integer" }, "imageId": { "type": "string" }, "status": { "type": "string" } } }, "models.CreateCommentRequest": { "type": "object", "required": [ "content", "entity_id", "entity_type", "trip_id" ], "properties": { "content": { "type": "string", "minLength": 1 }, "entity_id": { "type": "string" }, "entity_type": { "enum": [ "activity", "pitch" ], "allOf": [ { "$ref": "#/definitions/models.EntityType" } ] }, "trip_id": { "type": "string" } } }, "models.CreateMembershipRequest": { "type": "object", "required": [ "budget_max", "budget_min", "trip_id", "user_id" ], "properties": { "budget_max": { "type": "integer", "minimum": 0 }, "budget_min": { "type": "integer", "minimum": 0 }, "is_admin": { "type": "boolean" }, "trip_id": { "type": "string" }, "user_id": { "type": "string" } } }, "models.CreateTripInviteRequest": { "type": "object", "properties": { "expires_at": { "type": "string" } } }, "models.CreateTripRequest": { "type": "object", "required": [ "budget_max", "budget_min", "name" ], "properties": { "budget_max": { "type": "integer", "minimum": 0 }, "budget_min": { "type": "integer", "minimum": 0 }, "cover_image_id": { "type": "string" }, "name": { "type": "string", "minLength": 1 } } }, "models.CreateUserRequest": { "type": "object", "required": [ "name", "phone_number", "username" ], "properties": { "name": { "type": "string", "minLength": 1 }, "phone_number": { "type": "string" }, "username": { "type": "string" } } }, "models.DayTime": { "type": "object", "properties": { "day": { "type": "integer" }, "time": { "type": "string" } } }, "models.EntityType": { "type": "string", "enum": [ "activity", "pitch" ], "x-enum-varnames": [ "Activity", "Pitch" ] }, "models.GetFileAllSizesResponse": { "type": "object", "properties": { "files": { "type": "array", "items": { "$ref": "#/definitions/models.GetFileResponse" } }, "imageId": { "type": "string" } } }, "models.GetFileResponse": { "type": "object", "properties": { "contentType": { "type": "string" }, "imageId": { "type": "string" }, "size": { "$ref": "#/definitions/models.ImageSize" }, "url": { "type": "string" } } }, "models.ImageSize": { "type": "string", "enum": [ "large", "medium", "small" ], "x-enum-varnames": [ "ImageSizeLarge", "ImageSizeMedium", "ImageSizeSmall" ] }, "models.LatLng": { "type": "object", "properties": { "lat": { "type": "number" }, "lng": { "type": "number" } } }, "models.MatchedSubstring": { "type": "object", "properties": { "length": { "type": "integer" }, "offset": { "type": "integer" } } }, "models.Membership": { "type": "object", "properties": { "availability": { "type": "object", "additionalProperties": true }, "budget_max": { "type": "integer" }, "budget_min": { "type": "integer" }, "created_at": { "type": "string" }, "is_admin": { "type": "boolean" }, "trip_id": { "type": "string" }, "updated_at": { "type": "string" }, "user_id": { "type": "string" } } }, "models.MembershipAPIResponse": { "type": "object", "properties": { "availability": { "type": "object", "additionalProperties": true }, "budget_max": { "type": "integer" }, "budget_min": { "type": "integer" }, "created_at": { "type": "string" }, "is_admin": { "type": "boolean" }, "profile_picture_url": { "type": "string" }, "trip_id": { "type": "string" }, "updated_at": { "type": "string" }, "user_id": { "type": "string" }, "username": { "type": "string" } } }, "models.MembershipCursorPageResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/definitions/models.MembershipAPIResponse" } }, "limit": { "type": "integer" }, "next_cursor": { "type": "string" } } }, "models.NotificationError": { "type": "object", "properties": { "message": { "type": "string" }, "token": { "type": "string" }, "user_id": { "type": "string" } } }, "models.NotificationResponse": { "type": "object", "properties": { "errors": { "type": "array", "items": { "$ref": "#/definitions/models.NotificationError" } }, "failure_count": { "type": "integer" }, "success_count": { "type": "integer" } } }, "models.OpeningHours": { "type": "object", "properties": { "open_now": { "type": "boolean" }, "periods": { "type": "array", "items": { "$ref": "#/definitions/models.Period" } }, "weekday_text": { "type": "array", "items": { "type": "string" } } } }, "models.PaginatedCommentsResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/definitions/models.CommentAPIResponse" } }, "limit": { "type": "integer" }, "next_cursor": { "type": "string" } } }, "models.Period": { "type": "object", "properties": { "close": { "$ref": "#/definitions/models.DayTime" }, "open": { "$ref": "#/definitions/models.DayTime" } } }, "models.PlaceDetailsRequest": { "type": "object", "properties": { "fields": { "description": "Fields specifies which fields to return (optional)\nIf not specified, returns all available fields", "type": "array", "items": { "type": "string" } }, "input": { "description": "Input is the text to search for if PlaceID is not provided\nExample: \"Eiffel Tower\", \"Paris, France\"", "type": "string" }, "language": { "description": "Language is the language code for the results (optional)", "type": "string" }, "place_id": { "description": "PlaceID is the unique identifier for the place (from typeahead results)", "type": "string" } } }, "models.PlaceDetailsResponse": { "type": "object", "properties": { "address_components": { "type": "array", "items": { "$ref": "#/definitions/models.AddressComponent" } }, "formatted_address": { "description": "Address information", "type": "string" }, "formatted_phone_number": { "description": "Contact information", "type": "string" }, "geometry": { "description": "Location coordinates", "allOf": [ { "$ref": "#/definitions/models.PlaceGeometry" } ] }, "icon": { "type": "string" }, "international_phone_number": { "type": "string" }, "name": { "type": "string" }, "opening_hours": { "description": "Business information", "allOf": [ { "$ref": "#/definitions/models.OpeningHours" } ] }, "photos": { "description": "Photos", "type": "array", "items": { "$ref": "#/definitions/models.PlacePhoto" } }, "place_id": { "type": "string" }, "price_level": { "type": "integer" }, "rating": { "description": "Ratings and reviews", "type": "number" }, "types": { "description": "Place types", "type": "array", "items": { "type": "string" } }, "user_ratings_total": { "type": "integer" }, "utc_offset": { "description": "Additional information", "type": "integer" }, "vicinity": { "type": "string" }, "website": { "type": "string" } } }, "models.PlaceGeometry": { "type": "object", "properties": { "location": { "$ref": "#/definitions/models.LatLng" }, "viewport": { "$ref": "#/definitions/models.Bounds" } } }, "models.PlacePhoto": { "type": "object", "properties": { "height": { "type": "integer" }, "html_attributions": { "type": "array", "items": { "type": "string" } }, "photo_reference": { "type": "string" }, "photo_url": { "description": "PhotoURL is a generated URL to fetch the photo (optional, generated by backend)", "type": "string" }, "width": { "type": "integer" } } }, "models.PlacePrediction": { "type": "object", "properties": { "description": { "description": "Description is the human-readable name for this place", "type": "string" }, "main_text": { "description": "StructuredFormatting contains the main text and secondary text", "type": "string" }, "matched_substrings": { "description": "MatchedSubstrings indicates which parts of the description match the input", "type": "array", "items": { "$ref": "#/definitions/models.MatchedSubstring" } }, "place_id": { "description": "PlaceID is the unique identifier for this place", "type": "string" }, "secondary_text": { "type": "string" }, "types": { "type": "array", "items": { "type": "string" } } } }, "models.PlacesSearchResponse": { "type": "object", "properties": { "predictions": { "type": "array", "items": { "$ref": "#/definitions/models.PlacePrediction" } }, "status": { "type": "string" } } }, "models.S3HealthCheckResponse": { "type": "object", "properties": { "bucketName": { "type": "string" }, "error": { "description": "Error contains the underlying error message when status is \"unhealthy\"", "type": "string" }, "region": { "type": "string" }, "status": { "type": "string" } } }, "models.SendBulkNotificationRequest": { "type": "object", "required": [ "body", "title", "user_ids" ], "properties": { "body": { "type": "string", "maxLength": 500, "minLength": 1 }, "data": { "type": "object", "additionalProperties": true }, "title": { "type": "string", "maxLength": 100, "minLength": 1 }, "user_ids": { "type": "array", "maxItems": 1000, "minItems": 1, "items": { "type": "string" } } } }, "models.SendNotificationRequest": { "type": "object", "required": [ "body", "title", "user_id" ], "properties": { "body": { "type": "string", "maxLength": 500, "minLength": 1 }, "data": { "type": "object", "additionalProperties": true }, "title": { "type": "string", "maxLength": 100, "minLength": 1 }, "user_id": { "type": "string" } } }, "models.SizedUploadURL": { "type": "object", "properties": { "size": { "$ref": "#/definitions/models.ImageSize" }, "url": { "type": "string" } } }, "models.Trip": { "type": "object", "properties": { "budget_max": { "type": "integer" }, "budget_min": { "type": "integer" }, "cover_image_id": { "type": "string" }, "created_at": { "type": "string" }, "id": { "type": "string" }, "name": { "type": "string" }, "updated_at": { "type": "string" } } }, "models.TripAPIResponse": { "type": "object", "properties": { "budget_max": { "type": "integer" }, "budget_min": { "type": "integer" }, "cover_image_url": { "type": "string" }, "created_at": { "type": "string" }, "id": { "type": "string" }, "name": { "type": "string" }, "updated_at": { "type": "string" } } }, "models.TripCursorPageResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/definitions/models.TripAPIResponse" } }, "limit": { "type": "integer" }, "next_cursor": { "type": "string" } } }, "models.TripInviteAPIResponse": { "type": "object", "properties": { "code": { "type": "string" }, "created_at": { "type": "string" }, "created_by": { "type": "string" }, "expires_at": { "type": "string" }, "id": { "type": "string" }, "is_revoked": { "type": "boolean" }, "join_url": { "type": "string" }, "trip_id": { "type": "string" } } }, "models.UpdateCommentRequest": { "type": "object", "required": [ "content" ], "properties": { "content": { "type": "string", "minLength": 1 } } }, "models.UpdateMembershipRequest": { "type": "object", "properties": { "budget_max": { "type": "integer", "minimum": 0 }, "budget_min": { "type": "integer", "minimum": 0 }, "is_admin": { "type": "boolean" } } }, "models.UpdateTripRequest": { "type": "object", "properties": { "budget_max": { "type": "integer", "minimum": 0 }, "budget_min": { "type": "integer", "minimum": 0 }, "cover_image_id": { "type": "string" }, "name": { "type": "string", "minLength": 1 } } }, "models.UpdateUserRequest": { "type": "object", "properties": { "device_token": { "type": "string", "maxLength": 200 }, "name": { "type": "string", "minLength": 1 }, "phone_number": { "type": "string" }, "profile_picture": { "type": "string" }, "timezone": { "type": "string" }, "username": { "type": "string" } } }, "models.UploadURLRequest": { "type": "object", "required": [ "contentType", "fileKey", "sizes" ], "properties": { "contentType": { "type": "string", "minLength": 1 }, "fileKey": { "type": "string", "minLength": 1 }, "sizes": { "type": "array", "maxItems": 3, "minItems": 1, "items": { "$ref": "#/definitions/models.ImageSize" } } } }, "models.UploadURLResponse": { "type": "object", "properties": { "expiresAt": { "type": "string" }, "fileKey": { "type": "string" }, "imageId": { "type": "string" }, "uploadUrls": { "type": "array", "items": { "$ref": "#/definitions/models.SizedUploadURL" } } } }, "models.User": { "type": "object", "properties": { "created_at": { "type": "string" }, "device_token": { "type": "string" }, "device_token_updated_at": { "type": "string" }, "id": { "type": "string" }, "name": { "type": "string" }, "phone_number": { "type": "string" }, "profile_picture": { "type": "string" }, "timezone": { "type": "string" }, "updated_at": { "type": "string" }, "username": { "type": "string" } } } } }