Proyecto Sitios Históricos API
API para explorar sitios históricos, gestionar el perfil de usuario y crear/consultar reseñas.
Nota
Recordá que estos endpoints son los mínimos que nos imaginamos desde la cátedra como necesarios.
Si dentro del grupo o el docente asignado consideran que es necesario agregar algún otro debido a las particularidades de la solución elegida deberán agregarlos.
Convenciones generales
Autenticación
Revisar los endpoints que requieren autenticación.
Formato
Todas las respuestas y solicitudes usan application/json; charset=utf-8
.
Paginación
Parámetros page
(≥1) y per_page
(1…100) en los endpoints que lo soportan.
Sitios Históricos ¶
Sitios Históricos ¶
Listar sitiosGET/sites{?name,description,city,province,tags,order_by,lat,long,radius,page,per_page}
Obtiene una lista de sitios históricos que coinciden con los criterios de búsqueda.
Información del endpoint
No requiere autenticación.
Example URI
- name
string
(optional) Example: fortínFiltra por nombre (búsqueda parcial, case-insensitive).
- description
string
(optional) Example: sigloFiltra por descripción (búsqueda parcial, case-insensitive).
- city
string
(optional) Example: CórdobaFiltra por ciudad (búsqueda exacta, case-insensitive).
- province
string
(optional) Example: CórdobaFiltra por provincia (búsqueda exacta, case-insensitive).
- tags
string
(optional) Example: militar,siglo-xixFiltra por etiquetas (múltiples separadas por comas).
- order_by
string
(optional) Example: latestOrdena los resultados.
Choices:
rating-5-1
rating-1-5
latest
oldest
- lat
number
(optional) Example: -31.4201Latitud para búsqueda geoespacial.
- long
number
(optional) Example: -64.1888Longitud para búsqueda geoespacial.
- radius
number
(optional) Example: 50Radio en kilómetros para la búsqueda geoespacial. Requiere
lat
ylong
.- page
number
(optional) Example: 1Número de página (mínimo 1).
- per_page
number
(optional) Example: 20Cantidad de elementos por página (1 a 100).
200
Headers
Content-Type: application/json
Body
{
"data": [
{
"id": 1,
"name": "Fortín San Martín",
"short_description": "Antiguo fortín militar...",
"description": "Construcción del siglo XIX...",
"city": "Córdoba",
"province": "Córdoba",
"country": "AR",
"lat": -31.4201,
"long": -64.1888,
"tags": [
"militar",
"siglo-xix"
],
"state_of_conservation": "excelente",
"inserted_at": "2024-03-15T14:30:00Z",
"updated_at": "2024-06-20T09:15:00Z"
}
],
"meta": {
"page": 1,
"per_page": 20,
"total": 57
}
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"data": {
"type": "array",
"description": "Lista de sitios históricos."
},
"meta": {
"type": "object",
"properties": {
"page": {
"type": "number",
"description": "Número de página actual."
},
"per_page": {
"type": "number",
"description": "Cantidad de elementos por página."
},
"total": {
"type": "number",
"description": "Total de elementos en la lista."
}
},
"description": "Información de paginación."
}
}
}
400
Headers
Content-Type: application/json
Body
{
"error": {
"code": "invalid_query",
"message": "Parameter validation failed",
"details": {
"lat": [
"Must be a valid latitude"
],
"long": [
"Must be a valid longitude"
],
"per_page": [
"Must be between 1 and 100"
]
}
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Crear Sitio Históricos ¶
Crear un sitio históricoPOST/sites
Crea un nuevo sitio histórico por un usuario de app pública.
Información del endpoint
Requiere autenticación.
Example URI
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Body
{
"id": 1,
"name": "Fortín de la Ciudadela",
"short_description": "Antiguo fortín militar...",
"description": "Un antiguo fortín del siglo XVIII",
"city": "Córdoba",
"province": "Córdoba",
"country": "AR",
"lat": -31.4201,
"long": -64.1888,
"tags": [
"militar",
"siglo-xviii"
],
"state_of_conservation": "malo",
"inserted_at": "2024-03-15T14:30:00Z",
"updated_at": "2024-06-20T09:15:00Z"
}
201
Headers
Content-Type: application/json
Body
{
"id": 10,
"name": "Fortín San Martín",
"short_description": "Antiguo fortín militar...",
"description": "Un antiguo fortín del siglo XVIII",
"city": "Córdoba",
"province": "Córdoba",
"country": "AR",
"lat": -31.4201,
"long": -64.1888,
"tags": [
"militar",
"siglo-xviii"
],
"state_of_conservation": "malo",
"inserted_at": "2024-03-15T14:30:00Z",
"updated_at": "2024-06-20T09:15:00Z",
"user_id": 5
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "ID del sitio histórico."
},
"name": {
"type": "string",
"description": "Nombre del sitio histórico."
},
"short_description": {
"type": "string",
"description": "Descripción corta del sitio histórico."
},
"description": {
"type": "string",
"description": "Descripción del sitio histórico."
},
"city": {
"type": "string",
"description": "Ciudad donde se ubica el sitio histórico."
},
"province": {
"type": "string",
"description": "Provincia donde se ubica"
},
"country": {
"type": "string",
"description": "Código ISO del país donde se encuentra el sitio histórico."
},
"lat": {
"type": "number",
"description": "Latitud del sitio histórico."
},
"long": {
"type": "number",
"description": "Longitud del sitio histórico."
},
"tags": {
"type": "array",
"description": "Etiquetas asociadas al sitio histórico."
},
"state_of_conservation": {
"type": "string",
"enum": [
"excelente",
"bueno",
"regular",
"malo"
],
"description": "Estado de conservación del sitio histórico."
},
"inserted_at": {
"type": "string",
"description": "Fecha de creación del registro."
},
"updated_at": {
"type": "string",
"description": "Fecha de última actualización del registro."
},
"user_id": {
"type": "number",
"description": "ID del usuario que creó el sitio histórico."
}
},
"required": [
"name",
"short_description",
"description",
"city",
"province",
"country",
"lat",
"long",
"tags",
"state_of_conservation",
"inserted_at",
"updated_at"
]
}
400
Headers
Content-Type: application/json
Body
{
"error": {
"code": "invalid_data",
"message": "Invalid input data",
"details": {
"name": [
"This field is required"
]
}
}
}
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Unauthorized"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Sitio Histórico ¶
Obtener un sitio históricoGET/sites/{site_id}
Obtiene detalles de un sitio histórico específico por su ID.
Información del endpoint
No requiere autenticación.
Example URI
- site_id
number
(required) Example: 10ID del sitio histórico.
200
Headers
Content-Type: application/json
Body
{
"id": 1,
"name": "Fortín San Martín",
"short_description": "Antiguo fortín militar...",
"description": "Construcción del siglo XIX...",
"city": "Córdoba",
"province": "Córdoba",
"country": "AR",
"lat": -31.4201,
"long": -64.1888,
"tags": [
"militar",
"siglo-xix"
],
"state_of_conservation": "excelente",
"inserted_at": "2024-03-15T14:30:00Z",
"updated_at": "2024-06-20T09:15:00Z"
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Identificador único del sitio histórico."
},
"name": {
"type": "string",
"description": "Nombre del sitio histórico."
},
"short_description": {
"type": "string",
"description": "Descripción corta del sitio histórico."
},
"description": {
"type": "string",
"description": "Descripción del sitio histórico."
},
"city": {
"type": "string",
"description": "Ciudad donde se encuentra el sitio histórico."
},
"province": {
"type": "string",
"description": "Provincia donde se encuentra el sitio histórico."
},
"country": {
"type": "string",
"description": "Código ISO del país donde se encuentra el sitio histórico."
},
"lat": {
"type": "number",
"description": "Latitud."
},
"long": {
"type": "number",
"description": "Longitud."
},
"tags": {
"type": "array",
"description": "Etiquetas asociadas al sitio histórico."
},
"state_of_conservation": {
"type": "string",
"enum": [
"excelente",
"bueno",
"regular",
"malo"
],
"description": "Estado de conservación del sitio histórico."
},
"inserted_at": {
"type": "string",
"description": "Fecha de creación del registro."
},
"updated_at": {
"type": "string",
"description": "Fecha de última actualización del registro."
}
},
"required": [
"name",
"short_description",
"description",
"city",
"province",
"country",
"lat",
"long",
"tags",
"state_of_conservation",
"inserted_at",
"updated_at"
]
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Site not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Autenticación ¶
Login ¶
Se requiere de un token de acceso para acceder a los endpoints de información de socio.
Obtener tokenPOST/api/auth
Permite obtener el un JSON Web Token válido para el usuario y password.
Example URI
Headers
Content-Type: application/json
Body
{
"user": "john.doe@mail.com",
"password": "QXR0mi38a2"
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"user": {
"type": "string",
"description": "User email address."
},
"password": {
"type": "string",
"description": "User password."
}
},
"required": [
"user",
"password"
]
}
200
Headers
Content-Type: application/json
Body
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"expires_in": 3600
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "JSON Web Token."
},
"expires_in": {
"type": "number",
"description": "Token expiration time in seconds."
}
}
}
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "invalid_credentials",
"message": "Credenciales inválidas."
}
}
Reseñas ¶
Reseñas de un sitio ¶
Listar reseñasGET/sites/{site_id}/reviews{?page,per_page}
Obtiene una lista paginada de reseñas para un sitio histórico específico.
Información del endpoint
Requiere autenticación.
Example URI
- site_id
number
(required) Example: 10ID del sitio histórico.
- page
number
(optional) Example: 1Número de página (mínimo 1).
- per_page
number
(optional) Example: 10Cantidad de elementos por página (1 a 100).
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
200
Headers
Content-Type: application/json
Body
{
"data": [
{
"id": 1,
"site_id": 1,
"rating": 5,
"comment": "Imperdible",
"inserted_at": "2025-01-05T10:20:00Z",
"updated_at": "2025-06-12T08:00:00Z"
},
{
"id": 2,
"site_id": 101,
"rating": 5,
"comment": "Excelente",
"inserted_at": "2025-01-05T10:20:00Z",
"updated_at": "2025-06-12T08:00:00Z"
},
{
"id": 3,
"site_id": 101,
"rating": 4,
"comment": "Muy bueno, pero falta señalización",
"inserted_at": "2025-01-05T10:20:00Z",
"updated_at": "2025-06-12T08:00:00Z"
}
],
"meta": {
"page": 1,
"per_page": 10,
"total": 35
}
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"data": {
"type": "array",
"description": "Lista de reseñas."
},
"meta": {
"type": "object",
"properties": {
"page": {
"type": "number",
"description": "Número de página actual."
},
"per_page": {
"type": "number",
"description": "Cantidad de elementos por página."
},
"total": {
"type": "number",
"description": "Total de reseñas."
}
},
"description": "Información de paginación."
}
}
}
400
Headers
Content-Type: application/json
Body
{ "error": { "code": "invalid_data", "message": "Invalid input data", "details": { "per_page": ["Must be between 1 and 100"] } } } }
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Site not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Crear reseñaPOST/sites/{site_id}/reviews
Crea una nueva reseña para un sitio histórico específico.
Información del endpoint
Requiere autenticación.
Example URI
- site_id
number
(required) Example: 101ID del sitio histórico.
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Body
{
"rating": 4,
"site_id": 101,
"comment": "Faltan carteles explicativos en el interior."
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"rating": {
"type": "number",
"description": "Calificación otorgada (1 a 5)."
},
"site_id": {
"type": "number",
"description": "ID del sitio histórico."
},
"comment": {
"type": "string",
"description": "Comentario adicional."
}
},
"required": [
"rating",
"site_id"
]
}
201
Headers
Content-Type: application/json
Body
{
"id": 5,
"site_id": 101,
"rating": 4,
"comment": "Faltan carteles explicativos en el interior.",
"inserted_at": "2025-01-05T10:20:00Z",
"updated_at": "2025-06-12T08:00:00Z"
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Identificador único de la reseña."
},
"site_id": {
"type": "number",
"description": "ID del sitio histórico."
},
"rating": {
"type": "number",
"description": "Calificación otorgada."
},
"comment": {
"type": "string",
"description": "Comentario adicional."
},
"inserted_at": {
"type": "string",
"description": "Fecha de creación de la reseña."
},
"updated_at": {
"type": "string",
"description": "Fecha de última actualización de la reseña."
}
},
"required": [
"id",
"site_id",
"rating",
"inserted_at",
"updated_at"
]
}
400
Headers
Content-Type: application/json
Body
{
"error": {
"code": "invalid_data",
"message": "Invalid input data",
"details": {
"rating": [
"Must be between 1 and 5"
]
}
}
}
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Site not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Reseña ¶
Obtener reseñaGET/sites/{site_id}/reviews/{review_id}
Obtiene una reseña existente por su ID.
Información del endpoint
Requiere autenticación.
Example URI
- site_id
number
(required) Example: 10ID del sitio histórico.
- review_id
number
(required) Example: 5ID de la reseña a obtener.
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
200
Headers
Content-Type: application/json
Body
{
"id": 1,
"site_id": 1,
"rating": 1,
"comment": "Imperdible",
"inserted_at": "2025-01-05T10:20:00Z",
"updated_at": "2025-06-12T08:00:00Z"
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Identificador único de la reseña."
},
"site_id": {
"type": "number",
"description": "Identificador del sitio histórico asociado."
},
"rating": {
"type": "number",
"enum": [
1,
2,
3,
4,
5
],
"description": "Calificación otorgada."
},
"comment": {
"type": "string",
"description": "Título de la reseña."
},
"inserted_at": {
"type": "string",
"description": "Fecha de creación de la reseña."
},
"updated_at": {
"type": "string",
"description": "Fecha de última actualización de la reseña."
}
},
"required": [
"id",
"site_id",
"rating",
"comment",
"inserted_at",
"updated_at"
]
}
400
Headers
Content-Type: application/json
Body
{
"error": {
"code": "invalid_data",
"message": "Invalid input data",
"details": {
"review_id": [
"Must be a positive integer"
]
}
}
}
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
403
Headers
Content-Type: application/json
Body
{
"error": {
"code": "forbidden",
"message": "You do not have permission to access this review"
}
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Site not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Eliminar reseña propiaDELETE/sites/{site_id}/reviews/{review_id}
Elimina una reseña existente por su ID.
Información del endpoint
Requiere autenticación.
Example URI
- site_id
number
(required) Example: 10ID del sitio histórico.
- review_id
number
(required) Example: 5ID de la reseña a eliminar.
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
204
Headers
Content-Type: no content
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
403
Headers
Content-Type: application/json
Body
{
"error": {
"code": "forbidden",
"message": "You do not have permission to delete this review"
}
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Review not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Favoritos ¶
Favorito de un sitio ¶
Representa la relación “el usuario autenticado marcó este sitio como favorito”.
Información del endpoint
Requiere autenticación (JWT en Authorization: Bearer <token>
).
Marcar como favoritoPUT/sites/{site_id}/favorite
Crea (o asegura) que el sitio esté marcado como favorito por el usuario autenticado.
Example URI
- site_id
number
(required) Example: 10ID del sitio histórico.
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
204
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Site not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Desmarcar como favoritoDELETE/sites/{site_id}/favorite
Elimina (o asegura que no exista) la relación de favorito.
Example URI
- site_id
number
(required) Example: 10ID del sitio histórico.
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
204
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
404
Headers
Content-Type: application/json
Body
{
"error": {
"code": "not_found",
"message": "Site not found"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
Propio Usuario ¶
Favoritos del usuario ¶
Lista paginada de sitios marcados como favoritos por el usuario autenticado.
Información del endpoint
Requiere autenticación.
Listar favoritosGET/me/favorites{?page,per_page}
Example URI
- page
number
(optional) Example: 1Mínimo 1.
- per_page
number
(optional) Example: 201…100.
Headers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
200
Headers
Content-Type: application/json
Body
{
"data": [
{
"id": 1,
"name": "Fortín San Martín",
"short_description": "Antiguo fortín militar...",
"description": "Construcción del siglo XIX...",
"city": "Córdoba",
"province": "Córdoba",
"country": "AR",
"lat": -31.4201,
"long": -64.1888,
"tags": [
"militar",
"siglo-xix"
],
"state_of_conservation": "excelente",
"inserted_at": "2024-03-15T14:30:00Z",
"updated_at": "2024-06-20T09:15:00Z"
}
],
"meta": {
"page": 1,
"per_page": 20,
"total": 5
}
}
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"data": {
"type": "array",
"description": "Sitios favoritos (sugerencia: incluir `inserted_at` de la relación)."
},
"meta": {
"type": "object",
"properties": {
"page": {
"type": "number"
},
"per_page": {
"type": "number"
},
"total": {
"type": "number"
}
}
}
}
}
401
Headers
Content-Type: application/json
Body
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}
500
Headers
Content-Type: application/json
Body
{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}