Saltar al contenido principal

Guía de Integración API

Esta guía está diseñada para desarrolladores que quieran integrar el sistema de reservas en sus aplicaciones externas.

Tabla de Contenidos​

  1. Autenticación
  2. Conceptos Básicos
  3. Flujos de Integración
  4. Ejemplos Prácticos
  5. Manejo de Errores
  6. Rate Limiting

Autenticación​

Headers Requeridos​

Todos los endpoints requieren que incluyas los siguientes headers:

Content-Type: application/json
Accept: application/json
Authorization: Bearer YOUR_TOKEN_HERE

O si usas autenticación basada en sesión:

Content-Type: application/json
Accept: application/json
Cookie: laravel_session=YOUR_SESSION_COOKIE

Obtener un Token​

  1. Login con Google OAuth:

    GET https://tudominio.com/auth/google
  2. Procesa el callback:

    GET https://tudominio.com/auth/google/callback?code=AUTHORIZATION_CODE
  3. El sistema retorna una sesión que puedes usar en las peticiones API

Renovación de Token​

Los tokens OAuth se renuevan automáticamente. Si trabajas con Sanctum tokens personalizados, éstos son válidos hasta su expiración.


Conceptos Básicos​

Base URL​

https://tudominio.com/api

Formatos de Respuesta​

Respuesta exitosa (200/201):

{
"data": {
"id": 1,
"name": "Sala A",
"status": "active"
}
}

Respuesta con paginación (200):

{
"data": [
{ "id": 1, "name": "Sala A" },
{ "id": 2, "name": "Sala B" }
],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 42,
"last_page": 3
}
}

Errores (400/422):

{
"message": "Validation failed",
"errors": {
"title": ["El título es requerido"],
"room_id": ["La sala no existe"]
}
}

Formatos de Fecha​

Todas las fechas deben ser en formato ISO 8601 con timezone:

2025-01-15T10:30:00Z // UTC
2025-01-15T10:30:00+02:00 // Con offset

Para respuestas, el sistema siempre retorna en UTC (Z).


Flujos de Integración​

Flujo 1: Reserva Simple​

Pasos:

  1. Obtener lista de salas:

    GET /api/rooms
  2. Verificar disponibilidad:

    GET /api/rooms/1/availability?start_datetime=2025-01-15T10:00:00Z&end_datetime=2025-01-15T11:00:00Z
  3. Crear reserva:

    POST /api/bookings
    {
    "title": "Reunión",
    "room_id": 1,
    "start_datetime": "2025-01-15T10:00:00Z",
    "end_datetime": "2025-01-15T11:00:00Z"
    }

Flujo 2: Integración con CRM​

Pasos:

  1. Buscar usuarios del sistema:

    GET /api/bookings/search-users?q=maria
  2. Crear reserva con invitados:

    POST /api/bookings
    {
    "title": "Reunión con cliente",
    "room_id": 1,
    "start_datetime": "2025-01-15T14:00:00Z",
    "end_datetime": "2025-01-15T15:00:00Z",
    "guests": ["maria@example.com"],
    "has_google_meet": true
    }
  3. Extraer Google Meet link:

    {
    "google_meet_link": "https://meet.google.com/abc-defg-hij"
    }

Flujo 3: Sincronización Bidireccional​

Nota: Los webhooks están disponibles como extensión personalizada.


Ejemplos Prácticos​

Ejemplo 1: JavaScript/Fetch​

Crear una reserva​

const createBooking = async (
title,
roomId,
startDateTime,
endDateTime,
guests = []
) => {
const response = await fetch("/api/bookings", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: "Bearer YOUR_TOKEN",
},
body: JSON.stringify({
title,
room_id: roomId,
start_datetime: startDateTime,
end_datetime: endDateTime,
has_google_meet: true,
guests,
}),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}

return response.json();
};

// Uso
try {
const booking = await createBooking(
"Reunión de Equipo",
1,
"2025-01-15T10:00:00Z",
"2025-01-15T11:00:00Z",
["maria@example.com"]
);

console.log("Reserva creada:", booking.data);
console.log("Google Meet:", booking.data.google_meet_link);
} catch (error) {
console.error("Error:", error.message);
}

Listar reservas con filtros​

const listBookings = async (
roomId = null,
startDate = null,
endDate = null
) => {
const params = new URLSearchParams();
if (roomId) params.append("room_id", roomId);
if (startDate) params.append("start_date", startDate);
if (endDate) params.append("end_date", endDate);

const response = await fetch(`/api/bookings?${params}`, {
headers: {
Authorization: "Bearer YOUR_TOKEN",
},
});

return response.json();
};

// Uso
const bookings = await listBookings(1, "2025-01-15", "2025-01-31");
console.log("Total reservas:", bookings.meta.total);

Ejemplo 2: Python/Requests​

Crear reserva y obtener Google Meet​

import requests
from datetime import datetime, timedelta

BASE_URL = 'https://tudominio.com/api'
TOKEN = 'YOUR_TOKEN'

headers = {
'Authorization': f'Bearer {TOKEN}',
'Content-Type': 'application/json'
}

def create_booking_with_meet(title, room_id, start_time, duration_minutes):
end_time = start_time + timedelta(minutes=duration_minutes)

payload = {
'title': title,
'room_id': room_id,
'start_datetime': start_time.isoformat() + 'Z',
'end_datetime': end_time.isoformat() + 'Z',
'has_google_meet': True
}

response = requests.post(
f'{BASE_URL}/bookings',
json=payload,
headers=headers
)

if response.status_code in [200, 201]:
data = response.json()
return {
'booking_id': data['data']['id'],
'google_meet_link': data['data'].get('google_meet_link'),
'status': data['data']['status']
}
else:
raise Exception(f'Error: {response.json()}')

# Uso
now = datetime.utcnow()
booking = create_booking_with_meet(
title='Reunión Python',
room_id=1,
start_time=now,
duration_minutes=30
)

print(f'Reserva: {booking["booking_id"]}')
print(f'Google Meet: {booking["google_meet_link"]}')

Verificar disponibilidad de sala​

def check_availability(room_id, start_datetime, end_datetime):
response = requests.get(
f'{BASE_URL}/rooms/{room_id}/availability',
params={
'start_datetime': start_datetime.isoformat() + 'Z',
'end_datetime': end_datetime.isoformat() + 'Z'
},
headers=headers
)

return response.json()

# Uso
availability = check_availability(1, now, now + timedelta(hours=1))
if availability['available']:
print(' Sala disponible')
else:
print(' Sala ocupada')

Ejemplo 3: PHP/cURL​

Crear reserva desde PHP​

<?php

$token = 'YOUR_TOKEN';
$baseUrl = 'https://tudominio.com/api';

function createBooking($title, $roomId, $startDateTime, $endDateTime, $guests = []) {
global $token, $baseUrl;

$payload = [
'title' => $title,
'room_id' => $roomId,
'start_datetime' => $startDateTime,
'end_datetime' => $endDateTime,
'has_google_meet' => true,
'guests' => $guests
];

$ch = curl_init($baseUrl . '/bookings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $token,
'Content-Type: application/json'
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload)
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if (in_array($httpCode, [200, 201])) {
return json_decode($response, true);
} else {
throw new Exception("Error: " . $response);
}
}

// Uso
try {
$booking = createBooking(
'Reunión Importante',
1,
'2025-01-15T15:00:00Z',
'2025-01-15T16:00:00Z',
['maria@example.com']
);

echo "Reserva creada: " . $booking['data']['id'] . "\n";
echo "Google Meet: " . $booking['data']['google_meet_link'] . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>

Manejo de Errores​

Códigos de Estado HTTP​

CódigoSignificadoAcción
200OKOperación exitosa
201CreatedRecurso creado
204No ContentEliminado sin contenido
400Bad RequestValidación fallida
401UnauthorizedToken ausente/inválido
403ForbiddenSin permisos
404Not FoundRecurso no existe
422Unprocessable EntityError de validación
429Too Many RequestsRate limit excedido
500Server ErrorError del servidor

Errores de Validación​

{
"message": "Validation failed",
"errors": {
"title": ["El título es requerido"],
"room_id": ["La sala no existe"],
"start_datetime": ["La hora debe ser después de ahora"]
}
}

Manejo en JavaScript:

try {
const response = await fetch("/api/bookings", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer TOKEN",
},
body: JSON.stringify(data),
});

if (!response.ok) {
const error = await response.json();

if (response.status === 422) {
// Errores de validación
console.error("Errores:", error.errors);
} else if (response.status === 401) {
// Token expirado o inválido
console.error("Autenticación fallida");
} else {
console.error("Error:", error.message);
}
}
} catch (error) {
console.error("Network error:", error);
}

Rate Limiting​

Límites Aplicados​

EndpointLímiteVentana
POST /api/me/phone61 minuto
POST /api/me/phone/verify-otp61 minuto
Otros endpoints601 minuto

Respuesta cuando se excede el límite​

{
"message": "Too many requests. Please try again in 60 seconds.",
"retry_after": 60
}

Status: 429 Too Many Requests

Headers adicionales:

Retry-After: 60
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642156800

Implementar backoff exponencial​

async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);

if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") || "60");
console.log(
`Rate limited. Retrying in ${retryAfter}s (attempt ${attempt}/${maxRetries})`
);

await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
continue;
}

return response;
}

throw new Error("Max retries exceeded");
}

Webhook Events (Extensión)​

info

Los webhooks están disponibles en versiones premium o pueden implementarse como extensión.

Eventos Disponibles​

booking.created // Nueva reserva
booking.updated // Reserva actualizada
booking.cancelled // Reserva cancelada
booking.deleted // Reserva eliminada
user.phone_verified // Teléfono verificado

Ejemplo de payload​

{
"event": "booking.created",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"id": 1,
"title": "Reunión",
"status": "confirmed",
"room": { "id": 1, "name": "Sala A" },
"start_datetime": "2025-01-15T11:00:00Z",
"google_meet_link": "https://meet.google.com/xxx"
}
}

Mejores Prácticas​

1. Manejo de Errores​

  • Siempre verifica el status code
  • Maneja timeout de red
  • Implementa reintentos con backoff

2. Performance​

  • Cachea resultados de GET cuando sea posible
  • Usa filtros en lugar de descargar todo
  • Agrupa operaciones cuando puedas

3. Seguridad​

  • Nunca expongas tokens en el cliente
  • Guarda tokens en variables de entorno
  • Usa HTTPS siempre
  • Valida datos antes de enviar

4. Desarrollo​

  • Usa variables de entorno para URLs
  • Implementa logging de requests
  • Prueba con todos los status codes
  • Versionado de API (para futuro)

5. Integración​

  • Documenta qué datos necesitas de cada endpoint
  • Prueba con datos reales
  • Implementa sincronización incremental
  • Usa webhooks para actualizaciones en tiempo real