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