Referencia de la API REST

En esta página

EmDash expone una API REST en /_emdash/api/ para gestión de contenido, subida de multimedia y operaciones de esquema.

Autenticación

Las solicitudes de API requieren autenticación mediante un token Bearer en el encabezado Authorization:

Authorization: Bearer <token>

Genere tokens a través de la interfaz de administración o programáticamente.

Formato de respuesta

Todas las respuestas siguen un formato consistente. Una respuesta exitosa envuelve el resultado en data:

{
  "success": true,
  "data": { ... }
}

Una respuesta de error incluye un código, mensaje y detalles opcionales:

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message",
    "details": { ... }
  }
}

Endpoints de contenido

Listar contenido

GET /_emdash/api/content/:collection

Parámetros

ParámetroTipoDescripción
collectionstringSlug de la colección (ruta)
cursorstringCursor de paginación (query)
limitnumberElementos por página (query, predeterminado: 50)
statusstringFiltrar por estado (query)
orderBystringCampo para ordenar (query)
orderstringDirección de orden: asc o desc (query)

Respuesta

{
  "success": true,
  "data": {
    "items": [
      {
        "id": "01HXK5MZSN...",
        "type": "posts",
        "slug": "hello-world",
        "data": { "title": "Hello World", ... },
        "status": "published",
        "createdAt": "2025-01-24T12:00:00Z",
        "updatedAt": "2025-01-24T12:00:00Z"
      }
    ],
    "nextCursor": "eyJpZCI6..."
  }
}

Obtener contenido

GET /_emdash/api/content/:collection/:id

Respuesta

{
  "success": true,
  "data": {
    "item": {
      "id": "01HXK5MZSN...",
      "type": "posts",
      "slug": "hello-world",
      "data": { "title": "Hello World", ... },
      "status": "published",
      "createdAt": "2025-01-24T12:00:00Z",
      "updatedAt": "2025-01-24T12:00:00Z"
    }
  }
}

Crear contenido

POST /_emdash/api/content/:collection
Content-Type: application/json

Cuerpo de la solicitud

{
  "data": {
    "title": "New Post",
    "content": [...]
  },
  "slug": "new-post",
  "status": "draft"
}

Respuesta

{
  "success": true,
  "data": {
    "item": { ... }
  }
}

Actualizar contenido

PUT /_emdash/api/content/:collection/:id
Content-Type: application/json

Cuerpo de la solicitud

{
	"data": {
		"title": "Updated Title"
	},
	"status": "published"
}

Eliminar contenido

DELETE /_emdash/api/content/:collection/:id

Respuesta

{
	"success": true,
	"data": {
		"success": true
	}
}

Endpoints de multimedia

Listar multimedia

GET /_emdash/api/media?includeUsage=1

Parámetros

ParámetroTipoDescripción
cursorstringCursor de paginación opaco
limitnumberElementos por página, de 1 a 100 (predeterminado: 50)
mimeTypestringFiltrar por uno o más tipos MIME separados por comas
qstringBúsqueda de nombre de archivo insensible a mayúsculas
includeUsage1Incluir resumen de usage con cobertura en cada elemento devuelto

Respuesta

{
	"data": {
		"items": [
			{
				"id": "01HXK5MZSN...",
				"filename": "photo.jpg",
				"mimeType": "image/jpeg",
				"size": 102400,
				"width": 1920,
				"height": 1080,
				"url": "/_emdash/api/media/file/uploads/photo.jpg",
				"createdAt": "2025-01-24T12:00:00Z",
				"usage": {
					"count": 3,
					"coverage": {
						"scope": "all_content_collections",
						"status": "complete"
					}
				}
			}
		],
		"nextCursor": "eyJpZCI6..."
	}
}

Obtener multimedia

GET /_emdash/api/media/:id?includeUsage=1

includeUsage es opcional tanto en listar como en obtener. Su único valor aceptado es 1. Cuando se omite, la propiedad usage se omite y el servidor no ejecuta consultas de uso.

Resúmenes de uso

usage.count es el número de filas de contenido o locales activos distintos de EmDash cuya fuente indexada actual seleccionada referencia el elemento multimedia. Las referencias repetidas y múltiples variantes de fuente para la misma entrada de contenido cuentan una vez. El contenido en la papelera no cuenta.

Un conteo numérico puede revelar contenido similar a borradores. Se devuelve solo cuando un usuario de sesión tiene content:read_drafts, o cuando un token de API tiene el scope admin y su usuario asociado también tiene ese permiso. Otros lectores de multimedia reciben usage.count: null; esto es una respuesta redactada exitosa, no un error.

Cada resumen solicitado incluye cobertura agregada para todas las colecciones de contenido actualmente registradas:

EstadoSignificado
completeCada colección registrada tiene cobertura de uso actual y completada
neverNinguna colección registrada ha completado una reparación de uso inicial
runningUna reparación de uso está actualmente en ejecución
partialLa cobertura es mixta o solo parte del alcance registrado fue indexado
failedLa cobertura falló en todo el alcance registrado
staleLa cobertura indexada está desactualizada
unknownLa cobertura almacenada contiene un estado que esta versión no reconoce

Solo complete soporta una declaración de cero completo con alcance dentro de los campos gestionados por EmDash descritos a continuación. Los conteos con cualquier otro estado son proyecciones indexadas y pueden sobre-reportar o sub-reportar. Incluso los resultados completos son consultivos durante escrituras concurrentes; las lecturas de uso no son un bloqueo transaccional y no deben usarse como garantía de eliminación.

Obtener detalles de uso de multimedia

GET /_emdash/api/media/:id/usage?limit=50&cursor=...

Este endpoint requiere media:read y content:read_drafts. Los llamadores autenticados por token también requieren el scope admin; el scope del token no omite los permisos del usuario asociado.

limit controla los grupos de entradas de contenido por página, de 1 a 100 (predeterminado: 50). La paginación nunca divide las fuentes o ocurrencias para un grupo de entrada devuelto.

{
	"data": {
		"items": [
			{
				"collection": "posts",
				"contentId": "01CONTENT...",
				"title": "Launch notes",
				"slug": "launch-notes",
				"locale": "en",
				"status": "published",
				"scheduledAt": null,
				"deletedAt": null,
				"sources": [
					{
						"variant": "columns",
						"occurrences": [
							{
								"fieldSlug": "hero",
								"fieldPath": "hero",
								"occurrenceIndex": 0,
								"referenceType": "image_field"
							}
						]
					}
				]
			}
		],
		"nextCursor": "eyJvcmRlclZhbHVlIjoicG9zdHMiLCJpZCI6IjAxLi4uIn0",
		"coverage": {
			"scope": "all_content_collections",
			"status": "complete"
		}
	}
}

Los detalles autorizados incluyen entradas activas y eliminadas. Un deletedAt no nulo identifica una entrada eliminada. Las fuentes son columns o draft_overlay; las ocurrencias identifican el campo soportado y la ruta sin exponer metadatos internos del índice.

El uso de multimedia cubre referencias de multimedia local en campos de imagen y archivo de nivel superior, campos de imagen de repetidor y bloques de imagen de Portable Text gestionados por las colecciones de contenido de EmDash. No escanea código personalizado, HTML renderizado, configuraciones, menús, widgets, datos privados de plugins, sitios externos o activos exclusivos del proveedor.

Crear multimedia

POST /_emdash/api/media
Content-Type: application/json

Cuerpo de la solicitud

{
	"filename": "photo.jpg",
	"mimeType": "image/jpeg",
	"size": 102400,
	"width": 1920,
	"height": 1080,
	"storageKey": "uploads/photo.jpg"
}

Actualizar multimedia

PUT /_emdash/api/media/:id
Content-Type: application/json

Cuerpo de la solicitud

{
	"alt": "Photo description",
	"caption": "Photo caption"
}

Eliminar multimedia

DELETE /_emdash/api/media/:id

Reparar uso de multimedia

POST /_emdash/api/admin/media-usage/repair
Content-Type: application/json
X-EmDash-Request: 1

Repara el índice de uso de multimedia de contenido para una colección o para todas las colecciones de contenido. Este es un endpoint de administrador/operador: los llamadores autenticados por sesión necesitan schema:manage, y los tokens Bearer deben tener el scope admin porque la ruta está bajo /_emdash/api/admin.

La reparación de todo el contenido se ejecuta sincrónicamente y secuencialmente en la versión actual. Puede ser costosa en sitios grandes, por lo que los llamadores deben activarla deliberadamente y esperar la respuesta.

Cuerpo de la solicitud

Reparar una colección:

{
	"scope": "collection",
	"collection": "posts"
}

Reparar todas las colecciones de contenido:

{
	"scope": "all"
}

El cuerpo de la solicitud es obligatorio. Slugs inválidos, claves de solicitud desconocidas, scope faltante y solicitudes sin cuerpo devuelven 400 en lugar de reparar todo el contenido por defecto.

Respuesta

El endpoint devuelve 200 cuando una invocación de reparación produce un resultado estructurado. Inspeccione data.status: failed y stale son estados del dominio de reparación, no errores de transporte.

{
	"data": {
		"status": "complete",
		"indexedSourceCount": 12,
		"failedSourceCount": 0,
		"skippedSourceCount": 0,
		"deletedSourceCount": 1,
		"collections": [
			{
				"collection": "posts",
				"status": "complete",
				"indexedSourceCount": 12,
				"failedSourceCount": 0,
				"skippedSourceCount": 0,
				"deletedSourceCount": 1,
				"lastErrorCode": null,
				"startedAt": "2026-07-07T12:00:00.000Z",
				"completedAt": "2026-07-07T12:00:01.000Z"
			}
		]
	}
}

Campos de respuesta de nivel superior:

CampoTipoDescripción
statuscomplete | partial | failed | staleEstado de reparación agregado
indexedSourceCountnumberFuentes indexadas durante la reparación
failedSourceCountnumberFuentes que fallaron durante la reparación
skippedSourceCountnumberFuentes omitidas, incluyendo conflictos obsoletos
deletedSourceCountnumberFilas de uso obsoletas eliminadas durante la reparación
collectionsarrayResúmenes de reparación por colección

Campos de resumen por colección:

CampoTipoDescripción
collectionstringSlug de la colección
statuscomplete | partial | failed | staleEstado de reparación de la colección
indexedSourceCountnumberFuentes indexadas para esta colección
failedSourceCountnumberFuentes que fallaron para esta colección
skippedSourceCountnumberFuentes omitidas para esta colección
deletedSourceCountnumberFilas de uso obsoletas eliminadas para esta colección
lastErrorCodestring | nullÚltimo error de reparación de colección, cuando disponible
startedAtstringHora de inicio de la reparación
completedAtstring | nullHora de finalización, o null para resultados obsoletos

Las colecciones desconocidas devuelven 200 con data.status: "failed" y un lastErrorCode por colección como COLLECTION_NOT_FOUND. Los errores de transporte siguen usando el sobre de error estándar, incluyendo 400, 401, 403, 413 y 500.

Obtener archivo multimedia

GET /_emdash/api/media/file/:key

Sirve el contenido real del archivo. Solo para almacenamiento local.

Endpoints de revisiones

Listar revisiones

GET /_emdash/api/content/:collection/:entryId/revisions

Parámetros

ParámetroTipoDescripción
limitnumberMáx. revisiones a devolver (predeterminado: 50)

Respuesta

{
  "success": true,
  "data": {
    "items": [
      {
        "id": "01HXK5MZSN...",
        "collection": "posts",
        "entryId": "01HXK5MZSN...",
        "data": { ... },
        "createdAt": "2025-01-24T12:00:00Z"
      }
    ],
    "total": 5
  }
}

Obtener revisión

GET /_emdash/api/revisions/:revisionId

Restaurar revisión

POST /_emdash/api/revisions/:revisionId/restore

Restaura el contenido al estado de esta revisión y crea una nueva revisión.

Endpoints de esquema

Listar colecciones

GET /_emdash/api/schema/collections

Respuesta

{
	"success": true,
	"data": {
		"items": [
			{
				"id": "01HXK5MZSN...",
				"slug": "posts",
				"label": "Posts",
				"labelSingular": "Post",
				"supports": ["drafts", "revisions", "preview"]
			}
		]
	}
}

Obtener colección

GET /_emdash/api/schema/collections/:slug

Parámetros

ParámetroTipoDescripción
includeFieldsbooleanIncluir definiciones de campos (query)

Crear colección

POST /_emdash/api/schema/collections
Content-Type: application/json

Cuerpo de la solicitud

{
	"slug": "products",
	"label": "Products",
	"labelSingular": "Product",
	"description": "Product catalog",
	"supports": ["drafts", "revisions"]
}

Actualizar colección

PUT /_emdash/api/schema/collections/:slug
Content-Type: application/json

Eliminar colección

DELETE /_emdash/api/schema/collections/:slug

Parámetros

ParámetroTipoDescripción
forcebooleanEliminar incluso si la colección tiene contenido (query)

Listar campos

GET /_emdash/api/schema/collections/:slug/fields

Crear campo

POST /_emdash/api/schema/collections/:slug/fields
Content-Type: application/json

Cuerpo de la solicitud

{
	"slug": "price",
	"label": "Price",
	"type": "number",
	"required": true,
	"validation": {
		"min": 0
	}
}

Actualizar campo

PUT /_emdash/api/schema/collections/:collectionSlug/fields/:fieldSlug
Content-Type: application/json

Eliminar campo

DELETE /_emdash/api/schema/collections/:collectionSlug/fields/:fieldSlug

Reordenar campos

POST /_emdash/api/schema/collections/:slug/fields/reorder
Content-Type: application/json

Cuerpo de la solicitud

{
	"fieldSlugs": ["title", "content", "author", "publishedAt"]
}

Exportación de esquema

Exportar esquema (JSON)

GET /_emdash/api/schema
Accept: application/json

Exportar esquema (TypeScript)

GET /_emdash/api/schema?format=typescript
Accept: text/typescript

Devuelve interfaces TypeScript para todas las colecciones.

Endpoints de plugins

Listar plugins

GET /_emdash/api/admin/plugins

Obtener plugin

GET /_emdash/api/admin/plugins/:id

Activar plugin

POST /_emdash/api/admin/plugins/:id/enable

Desactivar plugin

POST /_emdash/api/admin/plugins/:id/disable

Códigos de error

CódigoEstado HTTPDescripción
NOT_FOUND404Recurso no encontrado
VALIDATION_ERROR400Datos de entrada inválidos
UNAUTHORIZED401Token faltante o inválido
FORBIDDEN403Permisos insuficientes
CONTENT_LIST_ERROR500Error al listar contenido
CONTENT_CREATE_ERROR500Error al crear contenido
CONTENT_UPDATE_ERROR500Error al actualizar contenido
CONTENT_DELETE_ERROR500Error al eliminar contenido
MEDIA_LIST_ERROR500Error al listar multimedia
MEDIA_CREATE_ERROR500Error al crear multimedia
SCHEMA_CREATE_ERROR500Operación de esquema fallida
SLUG_CONFLICT409El slug ya existe
RESERVED_SLUG400El slug está reservado

Endpoints de búsqueda

Búsqueda global

GET /_emdash/api/search?q=hello+world

Parámetros

ParámetroTipoDescripción
qstringConsulta de búsqueda (requerido)
collectionsstringSlugs de colecciones separados por comas
statusstringFiltrar por estado (predeterminado: published)
limitnumberMáx. resultados (predeterminado: 20)
cursorstringCursor de paginación

Respuesta

{
  "success": true,
  "data": {
    "items": [
      {
        "collection": "posts",
        "id": "01HXK5MZSN...",
        "slug": "hello-world",
        "locale": "en",
        "title": "Hello World",
        "snippet": "...this is a <mark>hello</mark> <mark>world</mark> example...",
        "score": 0.95
      }
    ],
    "nextCursor": "eyJvZmZzZXQiOjIwfQ"
  }
}

Sugerencias de búsqueda

GET /_emdash/api/search/suggest?q=hel&limit=5

Devuelve títulos con coincidencia de prefijo para autocompletado.

Reconstruir índice de búsqueda

POST /_emdash/api/search/rebuild

Reconstruir el índice FTS para todas o colecciones específicas.

Estadísticas de búsqueda

GET /_emdash/api/search/stats

Devuelve conteos de documentos indexados por colección.

Endpoints de secciones

Listar secciones

GET /_emdash/api/sections
GET /_emdash/api/sections?source=theme
GET /_emdash/api/sections?search=newsletter

Obtener sección

GET /_emdash/api/sections/:slug

Crear sección

POST /_emdash/api/sections
Content-Type: application/json

{
  "slug": "my-section",
  "title": "My Section",
  "keywords": ["keyword1"],
  "content": [...]
}

Actualizar sección

PUT /_emdash/api/sections/:slug

Eliminar sección

DELETE /_emdash/api/sections/:slug

Endpoints de configuración

Obtener toda la configuración

GET /_emdash/api/settings

Actualizar configuración

POST /_emdash/api/settings
Content-Type: application/json

{
  "siteTitle": "My Site",
  "tagline": "A great site",
  "postsPerPage": 10
}

Endpoints de menús

Listar menús

GET /_emdash/api/menus

Obtener menú

GET /_emdash/api/menus/:name

Crear menú

POST /_emdash/api/menus
Content-Type: application/json

{
  "name": "main",
  "label": "Main Navigation",
  "items": []
}

Actualizar menú

PUT /_emdash/api/menus/:name

Eliminar menú

DELETE /_emdash/api/menus/:name

Agregar elemento de menú

POST /_emdash/api/menus/:name/items
Content-Type: application/json

{
  "label": "About",
  "url": "/about",
  "position": 0
}

Reordenar elementos de menú

POST /_emdash/api/menus/:name/reorder
Content-Type: application/json

{
  "itemIds": ["item_1", "item_2", "item_3"]
}

Endpoints de taxonomías

Listar definiciones de taxonomías

GET /_emdash/api/taxonomies

Crear taxonomía

POST /_emdash/api/taxonomies
Content-Type: application/json

{
  "name": "categories",
  "label": "Categories",
  "hierarchical": true,
  "collections": ["posts"]
}

Listar términos

GET /_emdash/api/taxonomies/:name/terms

Crear término

POST /_emdash/api/taxonomies/:name/terms
Content-Type: application/json

{
  "slug": "tutorials",
  "label": "Tutorials",
  "parentId": "term_abc",
  "description": "How-to guides"
}

Actualizar término

PUT /_emdash/api/taxonomies/:name/terms/:slug

Eliminar término

DELETE /_emdash/api/taxonomies/:name/terms/:slug

Establecer términos de entrada

POST /_emdash/api/content/:collection/:id/terms/:taxonomy
Content-Type: application/json

{
  "termIds": ["term_news", "term_featured"]
}

Endpoints de áreas de widgets

Listar áreas de widgets

GET /_emdash/api/widget-areas

Obtener área de widgets

GET /_emdash/api/widget-areas/:name

Crear área de widgets

POST /_emdash/api/widget-areas
Content-Type: application/json

{
  "name": "sidebar",
  "label": "Main Sidebar",
  "description": "Appears on posts"
}

Eliminar área de widgets

DELETE /_emdash/api/widget-areas/:name

Agregar widget

POST /_emdash/api/widget-areas/:name/widgets
Content-Type: application/json

{
  "type": "content",
  "title": "About",
  "content": [...]
}

Actualizar widget

PUT /_emdash/api/widget-areas/:name/widgets/:id

Eliminar widget

DELETE /_emdash/api/widget-areas/:name/widgets/:id

Reordenar widgets

POST /_emdash/api/widget-areas/:name/reorder
Content-Type: application/json

{
  "widgetIds": ["widget_1", "widget_2", "widget_3"]
}

Endpoints de gestión de usuarios

Listar usuarios

GET /_emdash/api/admin/users
GET /_emdash/api/admin/users?role=40
GET /_emdash/api/admin/users?search=john

Obtener usuario

GET /_emdash/api/admin/users/:id

Actualizar usuario

PUT /_emdash/api/admin/users/:id
Content-Type: application/json

{
  "name": "John Doe",
  "role": 40
}

Activar usuario

POST /_emdash/api/admin/users/:id/enable

Desactivar usuario

POST /_emdash/api/admin/users/:id/disable

Endpoints de autenticación

Estado de configuración

GET /_emdash/api/setup/status

Devuelve si la configuración está completa y si existen usuarios.

Inicio de sesión con Passkey

POST /_emdash/api/auth/passkey/options

Obtener opciones de autenticación WebAuthn.

POST /_emdash/api/auth/passkey/verify
Content-Type: application/json

{
  "id": "credential-id",
  "rawId": "...",
  "response": {...},
  "type": "public-key"
}

Verificar passkey y crear sesión.

POST /_emdash/api/auth/magic-link/send
Content-Type: application/json

{
  "email": "[email protected]"
}
GET /_emdash/api/auth/magic-link/verify?token=xxx

Cerrar sesión

POST /_emdash/api/auth/logout

Usuario actual

GET /_emdash/api/auth/me

Invitar usuario

POST /_emdash/api/auth/invite
Content-Type: application/json

{
  "email": "[email protected]",
  "role": 30
}

Gestión de Passkeys

GET /_emdash/api/auth/passkey

Listar passkeys del usuario.

POST /_emdash/api/auth/passkey/register/options
POST /_emdash/api/auth/passkey/register/verify

Registrar nuevo passkey.

PATCH /_emdash/api/auth/passkey/:id
Content-Type: application/json

{
  "name": "MacBook Pro"
}

Renombrar passkey.

DELETE /_emdash/api/auth/passkey/:id

Eliminar passkey.

Endpoints de importación

Analizar exportación de WordPress

POST /_emdash/api/import/wordpress/analyze
Content-Type: multipart/form-data

file: <WXR file>

Ejecutar importación de WordPress

POST /_emdash/api/import/wordpress/execute
Content-Type: application/json

{
  "analysisId": "...",
  "options": {
    "includeMedia": true,
    "includeTaxonomies": true,
    "includeMenus": true
  }
}

Limitación de tasa

Los endpoints de API pueden estar limitados en tasa según la configuración del despliegue. Cuando se limita la tasa, las respuestas incluyen:

HTTP/1.1 429 Too Many Requests
Retry-After: 60

CORS

La API soporta CORS para solicitudes del navegador. Configure los orígenes permitidos en su despliegue.