Referencia de la API REST
DocPlatform expone una API JSON RESTful en /api/v1/. Todos los endpoints requieren autenticación a menos que se indique lo contrario.
URL base
http://localhost:3000/api/v1
Autenticación
La mayoría de los endpoints requieren un token de acceso JWT en el header Authorization:
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Obtenga tokens mediante los endpoints de login o OIDC.
Ciclo de vida del token
| Token | Tiempo de vida | Propósito |
|---|---|---|
| Token de acceso | 15 minutos | Autenticación de API |
| Token de actualización | 30 días | Obtener nuevos tokens de acceso |
Endpoints de autenticación
Registro
POST /api/v1/auth/register
Cree una nueva cuenta de usuario. El primer usuario se convierte en Super Admin.
Solicitud:
{
"name": "Jane Smith",
"email": "[email protected]",
"password": "secure-password-here"
}
Respuesta: 201 Created
{
"user": {
"id": "01HJK...",
"name": "Jane Smith",
"email": "[email protected]",
"role": "superadmin"
},
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"expires_in": 900
}
Inicio de sesión
POST /api/v1/auth/login
Autentíquese con correo electrónico y contraseña.
Solicitud:
{
"email": "[email protected]",
"password": "secure-password-here"
}
Respuesta: 200 OK
{
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"expires_in": 900
}
Errores:
| Código | Descripción |
|---|---|
401 |
Credenciales inválidas |
429 |
Demasiados intentos de inicio de sesión (limitación de tasa) |
Actualizar token
POST /api/v1/auth/refresh
Intercambie un token de actualización por un nuevo token de acceso. El token de actualización se rota (el anterior se invalida).
Solicitud:
{
"refresh_token": "eyJhbG..."
}
Respuesta: 200 OK
{
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"expires_in": 900
}
Solicitud de restablecimiento de contraseña
POST /api/v1/auth/password-reset
Solicite un token de restablecimiento de contraseña. Con SMTP configurado, se envía un correo electrónico. Sin SMTP, el token se registra en stdout.
Solicitud:
{
"email": "[email protected]"
}
Respuesta: 200 OK (siempre, independientemente de si el correo existe — previene enumeración)
Confirmación de restablecimiento de contraseña
POST /api/v1/auth/password-reset/confirm
Establezca una nueva contraseña usando un token de restablecimiento.
Solicitud:
{
"token": "reset-token-here",
"new_password": "new-secure-password"
}
Respuesta: 200 OK
Endpoints de contenido
Todos los endpoints de contenido están limitados a un workspace.
Listar páginas
GET /api/v1/workspaces/{workspace_id}/pages
Devuelve todas las páginas que el usuario actual tiene permiso para ver.
Parámetros de consulta:
| Parámetro | Tipo | Descripción |
|---|---|---|
parent_id |
string | Filtrar por página padre (para navegación en árbol) |
tag |
string | Filtrar por etiqueta |
published |
boolean | Filtrar por estado de publicación |
limit |
int | Máximo de resultados (predeterminado: 100) |
offset |
int | Desplazamiento de paginación |
Respuesta: 200 OK
{
"pages": [
{
"id": "01HJK...",
"title": "Getting Started",
"slug": "getting-started",
"description": "Install and configure DocPlatform.",
"path": "getting-started.md",
"tags": ["guide"],
"published": true,
"access": "public",
"created_at": "2026-03-08T10:00:00Z",
"updated_at": "2025-01-16T14:30:00Z",
"author_id": "01HJK..."
}
],
"total": 42,
"limit": 100,
"offset": 0
}
Obtener página
GET /api/v1/workspaces/{workspace_id}/pages/{page_id}
Respuesta: 200 OK
{
"id": "01HJK...",
"title": "Getting Started",
"slug": "getting-started",
"description": "Install and configure DocPlatform.",
"path": "getting-started.md",
"content": "# Getting Started\n\nThis guide walks you through...",
"content_hash": "sha256:abc123...",
"tags": ["guide"],
"published": true,
"access": "public",
"parent_id": null,
"created_at": "2026-03-08T10:00:00Z",
"updated_at": "2025-01-16T14:30:00Z",
"author_id": "01HJK..."
}
Errores:
| Código | Descripción |
|---|---|
403 |
Permisos insuficientes |
404 |
Página no encontrada |
Crear página
POST /api/v1/workspaces/{workspace_id}/pages
Solicitud:
{
"title": "New Page",
"slug": "new-page",
"content": "# New Page\n\nContent here...",
"description": "Description for search and SEO.",
"tags": ["guide"],
"published": false,
"parent_id": null
}
Respuesta: 201 Created — devuelve el objeto completo de la página.
Actualizar página
PUT /api/v1/workspaces/{workspace_id}/pages/{page_id}
Solicitud:
{
"title": "Updated Title",
"content": "# Updated Title\n\nUpdated content...",
"content_hash": "sha256:abc123..."
}
El campo content_hash habilita concurrencia optimista. Si el hash no coincide con la versión actual, el servidor devuelve 409 Conflict.
Respuesta: 200 OK — devuelve el objeto de página actualizado.
Errores:
| Código | Descripción |
|---|---|
409 |
Hash de contenido no coincide (edición concurrente detectada) |
Eliminar página
DELETE /api/v1/workspaces/{workspace_id}/pages/{page_id}
Respuesta: 204 No Content
Endpoints de workspace
Listar workspaces
GET /api/v1/workspaces
Devuelve los workspaces de los que el usuario actual es miembro.
Crear workspace
POST /api/v1/workspaces
Requiere rol Super Admin.
Solicitud:
{
"name": "API Docs",
"slug": "api-docs",
"git_remote": "[email protected]:org/api-docs.git",
"git_branch": "main"
}
Miembros del workspace
GET /api/v1/workspaces/{workspace_id}/members
POST /api/v1/workspaces/{workspace_id}/invitations
DELETE /api/v1/workspaces/{workspace_id}/members/{user_id}
PUT /api/v1/workspaces/{workspace_id}/members/{user_id}/role
Búsqueda
GET /api/v1/workspaces/{workspace_id}/search?q={query}
Parámetros de consulta:
| Parámetro | Tipo | Descripción |
|---|---|---|
q |
string | Consulta de búsqueda (obligatorio) |
tag |
string | Filtrar por etiqueta |
limit |
int | Máximo de resultados (predeterminado: 20) |
Respuesta: 200 OK
{
"results": [
{
"page_id": "01HJK...",
"title": "Git Integration",
"description": "Bidirectional git sync...",
"path": "guides/git-integration.md",
"score": 0.95,
"highlights": [
"...bidirectional <mark>git sync</mark> lets your team..."
]
}
],
"total": 5,
"query": "git sync",
"took_ms": 12
}
Los resultados están filtrados por permisos — los usuarios solo ven páginas a las que tienen acceso.
Sincronización git
Activar sincronización
POST /api/v1/workspaces/{workspace_id}/sync
Active manualmente un git pull + reconciliación. Requiere rol Admin.
Respuesta: 200 OK
{
"status": "completed",
"changes": {
"added": 2,
"updated": 1,
"deleted": 0
}
}
Endpoints de webhook
POST /api/v1/webhooks/github
POST /api/v1/webhooks/gitlab
POST /api/v1/webhooks/bitbucket
Estos endpoints reciben payloads de eventos push de proveedores de hosting git. No requieren header de autenticación — validan usando el secreto compartido GIT_WEBHOOK_SECRET.
Salud
Estos endpoints no requieren autenticación.
GET /health → 200 OK { "status": "ok" }
GET /ready → 200 OK { "status": "ready", "db": "ok", "search": "ok" }
Formato de errores
Todas las respuestas de error usan un formato consistente:
{
"error": {
"code": "CONFLICT",
"message": "Content hash mismatch. The page was modified by another user.",
"details": {
"current_hash": "sha256:def456...",
"provided_hash": "sha256:abc123..."
}
}
}
Códigos de error comunes
| HTTP | Código | Descripción |
|---|---|---|
400 |
BAD_REQUEST |
Cuerpo de solicitud o parámetros inválidos |
401 |
UNAUTHORIZED |
Autenticación faltante o inválida |
403 |
FORBIDDEN |
Permisos insuficientes |
404 |
NOT_FOUND |
Recurso no encontrado |
409 |
CONFLICT |
Modificación concurrente detectada |
429 |
RATE_LIMITED |
Demasiadas solicitudes |
500 |
INTERNAL_ERROR |
Error del servidor (verifique los logs) |
Paginación
Los endpoints de listado de contenido usan paginación basada en cursor con ULIDs para resultados estables incluso cuando se agrega o elimina contenido.
Parámetros de consulta:
| Parámetro | Tipo | Predeterminado | Descripción |
|---|---|---|---|
cursor |
string | — | ULID del último elemento de la página anterior. Omita para la primera página. |
limit |
int | 20 | Número de resultados por página (máximo: 100) |
Metadatos de respuesta:
{
"data": [...],
"next_cursor": "01HJK...",
"has_more": true
}
Pase next_cursor como el parámetro cursor en la siguiente solicitud. Cuando has_more es false, ha llegado al final.
Subida de assets
POST /api/v1/workspaces/{workspace_id}/assets
Suba imágenes y archivos a un workspace. Los assets se almacenan en el directorio assets/ del workspace y se confirman en git si la sincronización está habilitada.
Solicitud: multipart/form-data con un campo file.
Límites:
| Restricción | Valor |
|---|---|
| Tamaño máximo de archivo | 10 MB |
| Tipos aceptados | PNG, JPG, GIF, SVG, WebP, PDF |
Respuesta: 201 Created
{
"path": "assets/screenshot-2025-01-15.png",
"url": "/api/v1/workspaces/{workspace_id}/assets/screenshot-2025-01-15.png",
"size": 245760,
"content_type": "image/png"
}
Resolución de conflictos
Listar conflictos
GET /api/v1/workspaces/{workspace_id}/conflicts
Respuesta: 200 OK
{
"workspace_id": "01HJK...",
"sync_status": "conflict",
"conflicts": [
{
"path": "guides/editor.md",
"ours_hash": "abc123...",
"theirs_hash": "def456...",
"page_id": "01HJK...",
"timestamp": "20250115T103045Z"
}
]
}
Descargar una versión del conflicto
GET /api/v1/conflicts/{page_id}/{timestamp}/{version}
El parámetro version es ours (local) o theirs (remoto).
Respuesta: 200 OK con Content-Type: text/markdown — contenido del archivo sin procesar.
Resolver un conflicto
POST /api/v1/conflicts/{page_id}/{timestamp}/resolve
Elimina los artefactos de conflicto después de la resolución manual.
Respuesta: 200 OK
{
"message": "Conflict resolved",
"page_id": "01HJK..."
}
WebSocket
Obtener un ticket de conexión
POST /api/v1/auth/ws-ticket
Las conexiones WebSocket usan un patrón de ticket de un solo uso para evitar exponer tokens JWT en URLs.
Respuesta: 200 OK
{
"ticket": "random-ticket-value",
"expires_in": 30
}
El ticket es válido durante 30 segundos y solo puede usarse una vez. Conéctese mediante:
ws://localhost:3000/ws?ticket={ticket}
Eventos del servidor
| Tipo de evento | Payload | Cuándo |
|---|---|---|
page-created |
{workspace_id, path, actor} |
Se crea una nueva página |
page-updated |
{workspace_id, path, actor} |
Se modifica una página |
page-deleted |
{workspace_id, path, actor} |
Se elimina una página |
presence-join |
{workspace_id, user_id} |
Un usuario se conecta |
presence-leave |
{workspace_id, user_id} |
Un usuario se desconecta (timeout de 90s) |
sync-status |
{workspace_id, status} |
Cambio de estado de sincronización git |
conflict-detected |
{workspace_id, path} |
Se encuentra un conflicto de merge en git |
bulk-sync |
{workspace_id, changed_count, paths[]} |
Múltiples archivos sincronizados (>20 archivos) |
Mensajes del cliente
{"type": "subscribe", "workspace_id": "01HJK..."}
{"type": "unsubscribe", "workspace_id": "01HJK..."}
Headers de seguridad
DocPlatform establece los siguientes headers en todas las respuestas:
| Header | Valor |
|---|---|
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Content-Security-Policy |
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' |
X-Request-ID |
ULID (único por solicitud, incluido en respuestas de error y logs) |
La documentación publicada adicionalmente establece:
| Header | Valor |
|---|---|
Cache-Control |
public, max-age=300 |
ETag |
Hash del contenido de la página renderizada |
Limitación de tasa
| Categoría de endpoint | Community Edition |
|---|---|
| Operaciones de lectura | 100 / minuto por usuario |
| Operaciones de escritura | 20 / minuto por usuario |
| Búsqueda | 30 / minuto por usuario |
| Autenticación (login, registro, restablecimiento) | 5 / minuto por IP |
| Webhooks de git | 10 / minuto por workspace |
| Documentación publicada (pública) | 1.000 / minuto por IP |
Las respuestas de limitación de tasa incluyen los headers Retry-After (segundos) y X-RateLimit-Reset (marca de tiempo Unix).