1. Convenciones de endpoints
Base URL
Todas las rutas de API siguen el patrón:
/api/v1/{recurso}
El prefijo /api/v1/ cumple dos funciones: separa las rutas de datos de las rutas HTML,
y permite introducir cambios breaking en /api/v2/ sin romper las apps que ya usan v1.
GET /api/v1/hospitales
GET /api/v1/centros-de-acopio
GET /api/v1/edificios-danados
GET /api/v1/personas-desaparecidas
GET /api/v1/hospitales?ciudad=Caracas&estado=Miranda
GET /api/v1/centros-de-acopio?acepta=Agua
GET /api/v1/edificios-danados?condicion=destruido
POST /api/v1/hospitales
POST /api/v1/centros-de-acopio
PATCH /api/v1/hospitales/{id} Nomenclatura
Los recursos van en plural y kebab-case:
- Bien:
/api/v1/centros-de-acopio - Evitar:
/api/getCentro,/api/CentroAcopio,/api/centro_acopio
El kebab-case es el estándar en URLs. El plural deja claro que el endpoint retorna una colección.
Filtros como query params
Los filtros van en query params, nunca en el path. Se pueden combinar múltiples filtros en un mismo request:
GET /api/v1/hospitales?ciudad=Caracas&estado=Miranda&activo=true
Los query params son más flexibles que los paths. Evitás la explosión combinatoria de rutas y la API es más fácil de documentar.
2. DTOs por recurso
Cada recurso tiene un contrato de datos propuesto. El objetivo es que si dos apps distintas tienen información de hospitales, cualquier consumidor pueda leer ambas sin adaptadores. Los campos obligatorios están marcados — el resto puede omitirse si no tenés el dato.
Campos comunes a todos los recursos
id (UUID v4), verificado (boolean — aprobado por un moderador),
fuente (URL o handle de quien reportó el dato).
Usar ISO 8601 para todos los timestamps: "2026-06-28T14:30:00Z".
Hospital
{
"id": "uuid",
"nombre": "string",
"direccion": "string",
"ciudad": "string",
"estado": "string (estado venezolano)",
"lat": "number | null",
"lng": "number | null",
"activo": "boolean",
"insumos": [
{
"nombre": "string",
"prioridad": "alta | media | baja",
"cantidad_estimada": "number | null"
}
],
"areas_afectadas": ["Emergencias", "UCI", "Pediatría"],
"fuente": "string (url o handle)",
"verificado": "boolean",
"ultima_actualizacion": "ISO 8601"
} insumos[].prioridad— usar "alta" para lo urgente, "baja" para lo que se puede esperarareas_afectadas— string[] libre, valores sugeridos: Emergencias, UCI, Pediatría, Cirugía, Farmacialat/lng— null si no tenés coordenadas exactas. No inventar.
Centro de acopio
{
"id": "uuid",
"nombre": "string",
"direccion": "string",
"ciudad": "string",
"estado": "string",
"lat": "number | null",
"lng": "number | null",
"horario": "string | null",
"activo": "boolean",
"acepta": ["Ropa", "Agua", "Medicamentos", "Alimentos"],
"contacto": "string (teléfono o Telegram)",
"fuente": "string",
"verificado": "boolean",
"ultima_actualizacion": "ISO 8601"
} acepta— valores sugeridos: Ropa, Agua, Medicamentos, Alimentos, Herramientas, Material de construcción, Artículos de higienehorario— string libre: "Lunes a viernes 8am-5pm" o null si se desconocecontacto— preferir Telegram (@usuario) o teléfono con formato internacional
Edificio dañado
{
"id": "uuid",
"direccion": "string",
"ciudad": "string",
"estado": "string",
"lat": "number | null",
"lng": "number | null",
"pisos": "number | null",
"apartamentos": "number | null",
"familias_estimadas": "number | null",
"condicion": "inhabitable | dañado | destruido",
"fuente": "string",
"verificado": "boolean",
"reportado_en": "ISO 8601"
} condicion— tres valores fijos:inhabitable(en pie pero no habitable),dañado(daños visibles, evaluación pendiente),destruido(colapso total o parcial)pisos/apartamentos/familias_estimadas— null si se desconoce. Nunca enviar 0 si el dato simplemente falta.reportado_enen lugar deultima_actualizacion— para edificios, la fecha de reporte es más útil que la de actualización
Persona desaparecida
{
"id": "uuid",
"nombre": "string",
"edad": "number | null",
"ultima_ubicacion_conocida": "string",
"ciudad": "string",
"estado": "string",
"contacto_familiar": "string",
"foto_url": "string | null",
"encontrado": "boolean",
"reportado_en": "ISO 8601"
} Consideraciones de privacidad
foto_url es opcional y nunca requerido. Considerar GDPR/LOPD si tu app almacena fotos.
El campo encontrado debe poder actualizarse con un PATCH rápido.
3. Formato de respuesta
Todas las respuestas exitosas tienen el mismo envelope. Esto facilita que el código consumidor sea genérico: siempre leer response.data, siempre leer response.meta.total.
{
"data": [...],
"meta": {
"total": 42,
"page": 1,
"per_page": 50
},
"error": null
} data— array siempre, incluso si retorna un solo recurso. Simplifica el código consumidor.meta.total— total de registros que matchean el filtro (sin paginar). Permite mostrar "42 hospitales" aunque solo traigas 50 a la vez.meta.per_page— máximo sugerido: 50. Evita timeouts en conexiones lentas.error— null en respuestas exitosas. Ver sección Errores para respuestas con fallo.
4. Códigos de error
Todos los errores retornan JSON con el mismo formato. Nunca retornar HTML en un error de API.
{
"error": {
"code": "MISSING_FIELD",
"message": "El campo 'ciudad' es requerido."
}
} | HTTP | error.code | Cuándo usarlo |
|---|---|---|
| 400 | MISSING_FIELD | Campo requerido faltante. Indicar el nombre del campo en message. |
| 400 | INVALID_VALUE | Valor que no corresponde al tipo o enum esperado. |
| 404 | NOT_FOUND | Recurso con ese ID no existe. |
| 429 | RATE_LIMIT | Demasiadas requests. Incluir Retry-After header con segundos de espera. |
| 500 | INTERNAL_ERROR | Error interno. No exponer detalles del stack trace. |
5. CORS y autenticación
APIs de lectura (GET)
- Sin autenticación. Las APIs de emergencia deben ser accesibles sin fricción.
- CORS:
Access-Control-Allow-Origin: *— permite que cualquier app frontend consuma tu API. - Cache: considerar
Cache-Control: public, max-age=60para reducir carga en el servidor.
APIs de escritura (POST/PATCH)
- Opción A — Sin auth, con moderación: cualquiera puede enviar datos, pero quedan en estado "pendiente" hasta que un moderador los aprueba. Es el modelo de este hub.
- Opción B — JWT para escritura verificada: los reporters se autentican y sus datos se publican directamente. Requiere sistema de onboarding para reporters.
Regla de emergencia
Nunca requerir API key para leer datos de emergencia. Una persona que necesita saber dónde hay un centro de acopio no puede esperar el proceso de obtener una clave de API.
¿Tenés sugerencias?
Estos lineamientos son una propuesta abierta. Si encontrás un caso que no está cubierto o querés proponer un cambio, abrí la conversación en el grupo de desarrolladores.
Sugerir cambios en Telegram →