Download OpenAPI specification:
API de solo lectura de la Plataforma Ciudadana. Publica el catálogo de unidades municipales y categorías de reporte, y los reportes que la ciudadanía hace por WhatsApp o por el centro de llamadas.
Todas las operaciones son GET y responden JSON.
Cada solicitud a /v1 lleva la clave en la cabecera X-API-Key:
curl -H "X-API-Key: pcd_…" https://<servidor>/v1/unities
El municipio emite una clave por consumidor. Es un secreto: va en el servidor que llama a la API, no en el código de una página web, donde cualquiera la puede leer. Una clave revocada deja de funcionar en a lo sumo un minuto.
Cada clave tiene un cupo de solicitudes por minuto. Las respuestas informan el estado en las cabeceras RateLimit y RateLimit-Policy; al superarlo, la API responde 429 con Retry-After, en segundos.
Una respuesta exitosa trae el resultado en data. Un listado paginado agrega nextCursor:
{
"data": [{ "unityId": 3, "name": "Aseo Municipal", "description": "…" }]
}
Los errores siguen Problem Details (RFC 9457) con Content-Type: application/problem+json. Un 400 lista en errors cada parámetro con problema, todos a la vez:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Uno o más parámetros son inválidos.",
"instance": "/v1/reports",
"errors": [
{ "parameter": "status", "message": "estado no reconocido" },
{ "parameter": "from", "message": "debe ser una fecha yyyy-MM-dd válida" }
]
}
| Estado | Cuándo |
|---|---|
400 |
Un parámetro no cumple su formato o no existe. Un parámetro mal escrito no se ignora: unityid en vez de unityId es un error. |
401 |
Falta X-API-Key, o la clave no es válida o fue revocada. |
404 |
El recurso no existe o no es público. |
429 |
Se superó el límite de solicitudes. Reintentar después de los segundos que indica Retry-After. |
503 |
El servicio no puede leer los datos en este momento. Conviene reintentar con espera creciente. |
Un 503 o un 500 trae en detail una referencia para soporte. Cada respuesta lleva además el encabezado x-request-id; si la solicitud envía uno propio, se respeta.
Las respuestas llevan Cache-Control y ETag. Una solicitud con If-None-Match recibe 304 si el contenido no cambió. El catálogo se cachea cinco minutos y los reportes, uno.
Ningún endpoint publica datos de quien hizo un reporte: ni nombre, ni teléfono, ni fotos. Las coordenadas se redondean a unos 110 m y los reportes que el municipio rechazó no se publican.
Unidades municipales y las categorías de reporte que cada una atiende. Solo se publica lo activo.
Un reporte lleva unityId y categoryId: son los mismos identificadores de este catálogo, así que el catálogo sirve para traducirlos a nombres y para armar los filtros de Buscar reportes.
La unidad 0 es la de contingencia: recibe los reportes que no se pudieron asignar a una unidad municipal.
| unityId required | integer >= 0 Example: 3 Identificador de la unidad. |
{- "data": {
- "unityId": 3,
- "name": "Aseo Municipal",
- "description": "Recolección de basura y limpieza de vías públicas."
}
}Categorías activas, ordenadas por unidad y categoryId.
| unityId | integer >= 0 Example: unityId=3 Solo las categorías de esta unidad. |
{- "data": [
- {
- "categoryId": 12,
- "unityId": 3,
- "name": "Basura acumulada",
- "description": "Acumulación de desechos en la vía pública."
}
]
}| categoryId required | integer >= 0 Example: 12 Identificador de la categoría. |
{- "data": {
- "categoryId": 12,
- "unityId": 3,
- "name": "Basura acumulada",
- "description": "Acumulación de desechos en la vía pública."
}
}Los filtros se combinan entre sí. Por ejemplo, los reportes abiertos o en atención de la unidad 3 durante octubre:
GET /v1/reports?unityId=3&status=open,in_progress&from=2026-10-01&to=2026-10-31
from y to son días de calendario en la zona horaria del municipio, ambos inclusivos. to=2026-10-31 incluye todo el 31 de octubre.
El listado va del reporte más reciente al más antiguo y se pagina por cursor. Si nextCursor no es null, hay más reportes: se pide la página siguiente repitiendo los mismos filtros y pasando ese valor en cursor.
GET /v1/reports?unityId=3&limit=50
GET /v1/reports?unityId=3&limit=50&cursor=WzE3OTI...
El cursor marca una posición, no un número de página: un reporte que entra mientras se recorre el listado no corre las páginas ya leídas ni hace que un reporte salga dos veces. Es opaco; no conviene armarlo ni interpretarlo.
Reportes ciudadanos, del más reciente al más antiguo. Los filtros se combinan entre sí.
La paginación es por cursor: si nextCursor no es null, se pasa tal cual como cursor
con los mismos filtros para pedir la página siguiente. Un reporte nuevo no corre las páginas
ya leídas, como sí pasaría con un desplazamiento numérico.
Los reportes rechazados por el municipio no se publican.
| categoryId | integer >= 0 Example: categoryId=12 Solo los reportes de esta categoría. |
| unityId | integer >= 0 Example: unityId=3 Solo los reportes de esta unidad. |
| status | Array of strings non-empty Items Enum: "open" "in_progress" "resolved" "closed" Example: status=open&status=in_progress Uno o varios estados, separados por coma o repitiendo el parámetro. |
| from | string <date> Example: from=2026-10-01 Creados desde este día inclusive, en la zona horaria del municipio. |
| to | string <date> Example: to=2026-10-31 Creados hasta este día inclusive, en la zona horaria del municipio. |
| limit | integer [ 1 .. 100 ] Default: 25 Example: limit=25 Reportes por página. |
| cursor | string [ 1 .. 200 ] characters
|
{- "data": [
- {
- "id": "6650f1c2a9e4b3d2c1f0e9a8",
- "title": "Basura acumulada en la esquina",
- "description": "string",
- "status": "open",
- "categoryId": 12,
- "category": "Basura acumulada",
- "unityId": 3,
- "createdAt": "2026-10-28T15:04:05.000Z",
- "resolvedAt": "2019-08-24T14:15:22Z",
- "location": {
- "latitude": 14.072,
- "longitude": -87.192,
- "address": "Colonia Kennedy, frente al centro de salud"
}
}
], - "nextCursor": "string"
}| reportId required | string^[0-9a-fA-F]{24}$ Example: 6650f1c2a9e4b3d2c1f0e9a8 Identificador del reporte. |
{- "data": {
- "id": "6650f1c2a9e4b3d2c1f0e9a8",
- "title": "Basura acumulada en la esquina",
- "description": "string",
- "status": "open",
- "categoryId": 12,
- "category": "Basura acumulada",
- "unityId": 3,
- "createdAt": "2026-10-28T15:04:05.000Z",
- "resolvedAt": "2019-08-24T14:15:22Z",
- "location": {
- "latitude": 14.072,
- "longitude": -87.192,
- "address": "Colonia Kennedy, frente al centro de salud"
}
}
}