EmDash expose une API REST à /_emdash/api/ pour la gestion de contenu, l’upload de médias et les opérations de schéma.
Authentification
Les requêtes API nécessitent une authentification via un token Bearer dans l’en-tête Authorization :
Authorization: Bearer <token>
Générez des tokens via l’interface d’administration ou de manière programmatique.
Format de réponse
Toutes les réponses suivent un format cohérent. Une réponse réussie enveloppe le résultat dans data :
{
"success": true,
"data": { ... }
}
Une réponse d’erreur inclut un code, un message et des détails optionnels :
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": { ... }
}
}
Points de terminaison de contenu
Lister le contenu
GET /_emdash/api/content/:collection
Paramètres
| Paramètre | Type | Description |
|---|---|---|
collection | string | Slug de la collection (chemin) |
cursor | string | Curseur de pagination (query) |
limit | number | Éléments par page (query, par défaut : 50) |
status | string | Filtrer par statut (query) |
orderBy | string | Champ de tri (query) |
order | string | Direction du tri : asc ou desc (query) |
Réponse
{
"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..."
}
}
Obtenir du contenu
GET /_emdash/api/content/:collection/:id
Réponse
{
"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"
}
}
}
Créer du contenu
POST /_emdash/api/content/:collection
Content-Type: application/json
Corps de la requête
{
"data": {
"title": "New Post",
"content": [...]
},
"slug": "new-post",
"status": "draft"
}
Réponse
{
"success": true,
"data": {
"item": { ... }
}
}
Mettre à jour le contenu
PUT /_emdash/api/content/:collection/:id
Content-Type: application/json
Corps de la requête
{
"data": {
"title": "Updated Title"
},
"status": "published"
}
Supprimer du contenu
DELETE /_emdash/api/content/:collection/:id
Réponse
{
"success": true,
"data": {
"success": true
}
}
Points de terminaison des médias
Lister les médias
GET /_emdash/api/media?includeUsage=1
Paramètres
| Paramètre | Type | Description |
|---|---|---|
cursor | string | Curseur de pagination opaque |
limit | number | Éléments par page, de 1 à 100 (par défaut : 50) |
mimeType | string | Filtrer par un ou plusieurs types MIME séparés par des virgules |
q | string | Recherche de nom de fichier insensible à la casse |
includeUsage | 1 | Inclure un résumé d’usage avec couverture sur chaque élément retourné |
Réponse
{
"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..."
}
}
Obtenir un média
GET /_emdash/api/media/:id?includeUsage=1
includeUsage est optionnel pour la liste et l’obtention. Sa seule valeur acceptée est 1. Lorsqu’il est omis,
la propriété usage est omise et le serveur n’exécute pas de requêtes d’utilisation.
Résumés d’utilisation
usage.count est le nombre de lignes de contenu ou de locales actives distinctes d’EmDash dont la source
indexée actuelle sélectionnée référence l’élément média. Les références répétées et les multiples variantes
de source pour la même entrée de contenu comptent une fois. Le contenu dans la corbeille ne compte pas.
Un comptage numérique peut révéler du contenu de type brouillon. Il n’est retourné que lorsqu’un utilisateur de session a
content:read_drafts, ou lorsqu’un token API a le scope admin et que son utilisateur associé a également
cette permission. Les autres lecteurs de médias reçoivent usage.count: null ; c’est une réponse expurgée réussie,
pas une erreur.
Chaque résumé demandé inclut une couverture agrégée pour toutes les collections de contenu actuellement enregistrées :
| Statut | Signification |
|---|---|
complete | Chaque collection enregistrée a une couverture d’utilisation actuelle et complétée |
never | Aucune collection enregistrée n’a terminé une réparation d’utilisation initiale |
running | Une réparation d’utilisation est actuellement en cours |
partial | La couverture est mixte ou seule une partie du périmètre enregistré a été indexée |
failed | La couverture a échoué sur l’ensemble du périmètre enregistré |
stale | La couverture indexée est obsolète |
unknown | La couverture stockée contient un état que cette version ne reconnaît pas |
Seul complete supporte une déclaration de zéro complet avec périmètre dans les champs gérés par EmDash
décrits ci-dessous. Les comptages avec tout autre statut sont des projections indexées et peuvent sur-déclarer ou
sous-déclarer. Même les résultats complets sont consultatifs pendant les écritures concurrentes ; les lectures d’utilisation ne sont pas un
verrou transactionnel et ne doivent pas être utilisées comme garantie de suppression.
Obtenir les détails d’utilisation des médias
GET /_emdash/api/media/:id/usage?limit=50&cursor=...
Ce point de terminaison nécessite media:read et content:read_drafts. Les appelants authentifiés par token nécessitent
également le scope admin ; le scope du token ne contourne pas les permissions de l’utilisateur associé.
limit contrôle les groupes d’entrées de contenu par page, de 1 à 100 (par défaut : 50). La pagination ne
divise jamais les sources ou occurrences pour un groupe d’entrées retourné.
{
"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"
}
}
}
Les détails autorisés incluent les entrées actives et supprimées. Un deletedAt non nul identifie une
entrée dans la corbeille. Les sources sont columns ou draft_overlay ; les occurrences identifient le champ supporté
et le chemin sans exposer les métadonnées internes de l’index.
L’utilisation des médias couvre les références de médias locaux dans les champs d’image et de fichier de niveau supérieur, les champs d’image de répéteur et les blocs d’image Portable Text gérés par les collections de contenu EmDash. Elle ne scanne pas le code personnalisé, le HTML rendu, les paramètres, les menus, les widgets, les données privées des plugins, les sites externes ou les actifs exclusifs au fournisseur.
Créer un média
POST /_emdash/api/media
Content-Type: application/json
Corps de la requête
{
"filename": "photo.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"width": 1920,
"height": 1080,
"storageKey": "uploads/photo.jpg"
}
Mettre à jour un média
PUT /_emdash/api/media/:id
Content-Type: application/json
Corps de la requête
{
"alt": "Photo description",
"caption": "Photo caption"
}
Supprimer un média
DELETE /_emdash/api/media/:id
Réparer l’utilisation des médias
POST /_emdash/api/admin/media-usage/repair
Content-Type: application/json
X-EmDash-Request: 1
Répare l’index d’utilisation des médias de contenu pour une collection ou pour toutes les collections de contenu. C’est un point de terminaison administrateur/opérateur : les appelants authentifiés par session ont besoin de schema:manage, et les tokens Bearer doivent avoir le scope admin car la route est sous /_emdash/api/admin.
La réparation de tout le contenu s’exécute de manière synchrone et séquentielle dans la version actuelle. Elle peut être coûteuse sur les grands sites, les appelants doivent donc la déclencher délibérément et attendre la réponse.
Corps de la requête
Réparer une collection :
{
"scope": "collection",
"collection": "posts"
}
Réparer toutes les collections de contenu :
{
"scope": "all"
}
Le corps de la requête est obligatoire. Les slugs invalides, les clés de requête inconnues, le scope manquant et les requêtes sans corps retournent 400 au lieu de réparer tout le contenu par défaut.
Réponse
Le point de terminaison retourne 200 lorsqu’une invocation de réparation produit un résultat structuré. Inspectez data.status : failed et stale sont des statuts du domaine de réparation, pas des erreurs de transport.
{
"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"
}
]
}
}
Champs de réponse de niveau supérieur :
| Champ | Type | Description |
|---|---|---|
status | complete | partial | failed | stale | Statut de réparation agrégé |
indexedSourceCount | number | Sources indexées pendant la réparation |
failedSourceCount | number | Sources ayant échoué pendant la réparation |
skippedSourceCount | number | Sources ignorées, y compris les conflits obsolètes |
deletedSourceCount | number | Lignes d’utilisation obsolètes supprimées pendant la réparation |
collections | array | Résumés de réparation par collection |
Champs de résumé par collection :
| Champ | Type | Description |
|---|---|---|
collection | string | Slug de la collection |
status | complete | partial | failed | stale | Statut de réparation de la collection |
indexedSourceCount | number | Sources indexées pour cette collection |
failedSourceCount | number | Sources ayant échoué pour cette collection |
skippedSourceCount | number | Sources ignorées pour cette collection |
deletedSourceCount | number | Lignes d’utilisation obsolètes supprimées pour cette collection |
lastErrorCode | string | null | Dernière erreur de réparation de collection, si disponible |
startedAt | string | Heure de début de la réparation |
completedAt | string | null | Heure de fin, ou null pour les résultats obsolètes |
Les collections inconnues retournent 200 avec data.status: "failed" et un lastErrorCode par collection tel que COLLECTION_NOT_FOUND. Les erreurs de transport utilisent toujours l’enveloppe d’erreur standard, y compris 400, 401, 403, 413 et 500.
Obtenir un fichier média
GET /_emdash/api/media/file/:key
Sert le contenu réel du fichier. Pour le stockage local uniquement.
Points de terminaison des révisions
Lister les révisions
GET /_emdash/api/content/:collection/:entryId/revisions
Paramètres
| Paramètre | Type | Description |
|---|---|---|
limit | number | Max. de révisions à retourner (par défaut : 50) |
Réponse
{
"success": true,
"data": {
"items": [
{
"id": "01HXK5MZSN...",
"collection": "posts",
"entryId": "01HXK5MZSN...",
"data": { ... },
"createdAt": "2025-01-24T12:00:00Z"
}
],
"total": 5
}
}
Obtenir une révision
GET /_emdash/api/revisions/:revisionId
Restaurer une révision
POST /_emdash/api/revisions/:revisionId/restore
Restaure le contenu à l’état de cette révision et crée une nouvelle révision.
Points de terminaison du schéma
Lister les collections
GET /_emdash/api/schema/collections
Réponse
{
"success": true,
"data": {
"items": [
{
"id": "01HXK5MZSN...",
"slug": "posts",
"label": "Posts",
"labelSingular": "Post",
"supports": ["drafts", "revisions", "preview"]
}
]
}
}
Obtenir une collection
GET /_emdash/api/schema/collections/:slug
Paramètres
| Paramètre | Type | Description |
|---|---|---|
includeFields | boolean | Inclure les définitions de champs (query) |
Créer une collection
POST /_emdash/api/schema/collections
Content-Type: application/json
Corps de la requête
{
"slug": "products",
"label": "Products",
"labelSingular": "Product",
"description": "Product catalog",
"supports": ["drafts", "revisions"]
}
Mettre à jour une collection
PUT /_emdash/api/schema/collections/:slug
Content-Type: application/json
Supprimer une collection
DELETE /_emdash/api/schema/collections/:slug
Paramètres
| Paramètre | Type | Description |
|---|---|---|
force | boolean | Supprimer même si la collection a du contenu (query) |
Lister les champs
GET /_emdash/api/schema/collections/:slug/fields
Créer un champ
POST /_emdash/api/schema/collections/:slug/fields
Content-Type: application/json
Corps de la requête
{
"slug": "price",
"label": "Price",
"type": "number",
"required": true,
"validation": {
"min": 0
}
}
Mettre à jour un champ
PUT /_emdash/api/schema/collections/:collectionSlug/fields/:fieldSlug
Content-Type: application/json
Supprimer un champ
DELETE /_emdash/api/schema/collections/:collectionSlug/fields/:fieldSlug
Réordonner les champs
POST /_emdash/api/schema/collections/:slug/fields/reorder
Content-Type: application/json
Corps de la requête
{
"fieldSlugs": ["title", "content", "author", "publishedAt"]
}
Export de schéma
Exporter le schéma (JSON)
GET /_emdash/api/schema
Accept: application/json
Exporter le schéma (TypeScript)
GET /_emdash/api/schema?format=typescript
Accept: text/typescript
Retourne des interfaces TypeScript pour toutes les collections.
Points de terminaison des plugins
Lister les plugins
GET /_emdash/api/admin/plugins
Obtenir un plugin
GET /_emdash/api/admin/plugins/:id
Activer un plugin
POST /_emdash/api/admin/plugins/:id/enable
Désactiver un plugin
POST /_emdash/api/admin/plugins/:id/disable
Codes d’erreur
| Code | Statut HTTP | Description |
|---|---|---|
NOT_FOUND | 404 | Ressource non trouvée |
VALIDATION_ERROR | 400 | Données d’entrée invalides |
UNAUTHORIZED | 401 | Token manquant ou invalide |
FORBIDDEN | 403 | Permissions insuffisantes |
CONTENT_LIST_ERROR | 500 | Échec de la liste du contenu |
CONTENT_CREATE_ERROR | 500 | Échec de la création du contenu |
CONTENT_UPDATE_ERROR | 500 | Échec de la mise à jour du contenu |
CONTENT_DELETE_ERROR | 500 | Échec de la suppression du contenu |
MEDIA_LIST_ERROR | 500 | Échec de la liste des médias |
MEDIA_CREATE_ERROR | 500 | Échec de la création du média |
SCHEMA_CREATE_ERROR | 500 | Opération de schéma échouée |
SLUG_CONFLICT | 409 | Le slug existe déjà |
RESERVED_SLUG | 400 | Le slug est réservé |
Points de terminaison de recherche
Recherche globale
GET /_emdash/api/search?q=hello+world
Paramètres
| Paramètre | Type | Description |
|---|---|---|
q | string | Requête de recherche (obligatoire) |
collections | string | Slugs de collections séparés par des virgules |
status | string | Filtrer par statut (par défaut : published) |
limit | number | Max. de résultats (par défaut : 20) |
cursor | string | Curseur de pagination |
Réponse
{
"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"
}
}
Suggestions de recherche
GET /_emdash/api/search/suggest?q=hel&limit=5
Retourne des titres avec correspondance de préfixe pour l’autocomplétion.
Reconstruire l’index de recherche
POST /_emdash/api/search/rebuild
Reconstruire l’index FTS pour toutes ou certaines collections.
Statistiques de recherche
GET /_emdash/api/search/stats
Retourne le nombre de documents indexés par collection.
Points de terminaison des sections
Lister les sections
GET /_emdash/api/sections
GET /_emdash/api/sections?source=theme
GET /_emdash/api/sections?search=newsletter
Obtenir une section
GET /_emdash/api/sections/:slug
Créer une section
POST /_emdash/api/sections
Content-Type: application/json
{
"slug": "my-section",
"title": "My Section",
"keywords": ["keyword1"],
"content": [...]
}
Mettre à jour une section
PUT /_emdash/api/sections/:slug
Supprimer une section
DELETE /_emdash/api/sections/:slug
Points de terminaison des paramètres
Obtenir tous les paramètres
GET /_emdash/api/settings
Mettre à jour les paramètres
POST /_emdash/api/settings
Content-Type: application/json
{
"siteTitle": "My Site",
"tagline": "A great site",
"postsPerPage": 10
}
Points de terminaison des menus
Lister les menus
GET /_emdash/api/menus
Obtenir un menu
GET /_emdash/api/menus/:name
Créer un menu
POST /_emdash/api/menus
Content-Type: application/json
{
"name": "main",
"label": "Main Navigation",
"items": []
}
Mettre à jour un menu
PUT /_emdash/api/menus/:name
Supprimer un menu
DELETE /_emdash/api/menus/:name
Ajouter un élément de menu
POST /_emdash/api/menus/:name/items
Content-Type: application/json
{
"label": "About",
"url": "/about",
"position": 0
}
Réordonner les éléments de menu
POST /_emdash/api/menus/:name/reorder
Content-Type: application/json
{
"itemIds": ["item_1", "item_2", "item_3"]
}
Points de terminaison des taxonomies
Lister les définitions de taxonomies
GET /_emdash/api/taxonomies
Créer une taxonomie
POST /_emdash/api/taxonomies
Content-Type: application/json
{
"name": "categories",
"label": "Categories",
"hierarchical": true,
"collections": ["posts"]
}
Lister les termes
GET /_emdash/api/taxonomies/:name/terms
Créer un terme
POST /_emdash/api/taxonomies/:name/terms
Content-Type: application/json
{
"slug": "tutorials",
"label": "Tutorials",
"parentId": "term_abc",
"description": "How-to guides"
}
Mettre à jour un terme
PUT /_emdash/api/taxonomies/:name/terms/:slug
Supprimer un terme
DELETE /_emdash/api/taxonomies/:name/terms/:slug
Définir les termes d’une entrée
POST /_emdash/api/content/:collection/:id/terms/:taxonomy
Content-Type: application/json
{
"termIds": ["term_news", "term_featured"]
}
Points de terminaison des zones de widgets
Lister les zones de widgets
GET /_emdash/api/widget-areas
Obtenir une zone de widgets
GET /_emdash/api/widget-areas/:name
Créer une zone de widgets
POST /_emdash/api/widget-areas
Content-Type: application/json
{
"name": "sidebar",
"label": "Main Sidebar",
"description": "Appears on posts"
}
Supprimer une zone de widgets
DELETE /_emdash/api/widget-areas/:name
Ajouter un widget
POST /_emdash/api/widget-areas/:name/widgets
Content-Type: application/json
{
"type": "content",
"title": "About",
"content": [...]
}
Mettre à jour un widget
PUT /_emdash/api/widget-areas/:name/widgets/:id
Supprimer un widget
DELETE /_emdash/api/widget-areas/:name/widgets/:id
Réordonner les widgets
POST /_emdash/api/widget-areas/:name/reorder
Content-Type: application/json
{
"widgetIds": ["widget_1", "widget_2", "widget_3"]
}
Points de terminaison de gestion des utilisateurs
Lister les utilisateurs
GET /_emdash/api/admin/users
GET /_emdash/api/admin/users?role=40
GET /_emdash/api/admin/users?search=john
Obtenir un utilisateur
GET /_emdash/api/admin/users/:id
Mettre à jour un utilisateur
PUT /_emdash/api/admin/users/:id
Content-Type: application/json
{
"name": "John Doe",
"role": 40
}
Activer un utilisateur
POST /_emdash/api/admin/users/:id/enable
Désactiver un utilisateur
POST /_emdash/api/admin/users/:id/disable
Points de terminaison d’authentification
Statut de configuration
GET /_emdash/api/setup/status
Retourne si la configuration est terminée et si des utilisateurs existent.
Connexion par Passkey
POST /_emdash/api/auth/passkey/options
Obtenir les options d’authentification WebAuthn.
POST /_emdash/api/auth/passkey/verify
Content-Type: application/json
{
"id": "credential-id",
"rawId": "...",
"response": {...},
"type": "public-key"
}
Vérifier le passkey et créer une session.
Magic Link
POST /_emdash/api/auth/magic-link/send
Content-Type: application/json
{
"email": "[email protected]"
}
GET /_emdash/api/auth/magic-link/verify?token=xxx
Déconnexion
POST /_emdash/api/auth/logout
Utilisateur actuel
GET /_emdash/api/auth/me
Inviter un utilisateur
POST /_emdash/api/auth/invite
Content-Type: application/json
{
"email": "[email protected]",
"role": 30
}
Gestion des Passkeys
GET /_emdash/api/auth/passkey
Lister les passkeys de l’utilisateur.
POST /_emdash/api/auth/passkey/register/options
POST /_emdash/api/auth/passkey/register/verify
Enregistrer un nouveau passkey.
PATCH /_emdash/api/auth/passkey/:id
Content-Type: application/json
{
"name": "MacBook Pro"
}
Renommer un passkey.
DELETE /_emdash/api/auth/passkey/:id
Supprimer un passkey.
Points de terminaison d’importation
Analyser l’export WordPress
POST /_emdash/api/import/wordpress/analyze
Content-Type: multipart/form-data
file: <WXR file>
Exécuter l’importation WordPress
POST /_emdash/api/import/wordpress/execute
Content-Type: application/json
{
"analysisId": "...",
"options": {
"includeMedia": true,
"includeTaxonomies": true,
"includeMenus": true
}
}
Limitation de débit
Les points de terminaison API peuvent être limités en débit selon la configuration du déploiement. Lors d’une limitation de débit, les réponses incluent :
HTTP/1.1 429 Too Many Requests
Retry-After: 60
CORS
L’API supporte CORS pour les requêtes navigateur. Configurez les origines autorisées dans votre déploiement.