REST API 参考

本页内容

EmDash 在 /_emdash/api/ 暴露 REST API,用于内容管理、媒体上传和架构操作。

认证

API 请求需要通过 Authorization 头中的 Bearer 令牌进行认证:

Authorization: Bearer <token>

通过管理界面或编程方式生成令牌。

响应格式

所有响应遵循一致的格式。成功响应将结果包装在 data 中:

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

错误响应包含代码、消息和可选的详细信息:

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

内容端点

列出内容

GET /_emdash/api/content/:collection

参数

参数类型描述
collectionstring集合标识符(路径)
cursorstring分页游标(查询)
limitnumber每页条目数(查询,默认:50)
statusstring按状态筛选(查询)
orderBystring排序字段(查询)
orderstring排序方向:ascdesc(查询)

响应

{
  "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..."
  }
}

获取内容

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

响应

{
  "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"
    }
  }
}

创建内容

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

请求体

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

响应

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

更新内容

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

请求体

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

删除内容

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

响应

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

媒体端点

列出媒体

GET /_emdash/api/media?includeUsage=1

参数

参数类型描述
cursorstring不透明分页游标
limitnumber每页条目数,1 到 100(默认:50)
mimeTypestring按一个或多个逗号分隔的 MIME 类型筛选
qstring不区分大小写的文件名搜索
includeUsage1在每个返回项目上包含覆盖率感知的 usage 摘要

响应

{
	"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..."
	}
}

获取媒体

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

includeUsage 在列表和获取中都是可选的。其唯一接受的值为 1。省略时, usage 属性将被省略,服务器不会执行使用情况查询。

使用情况摘要

usage.count 是其所选当前索引源引用该媒体项目的 EmDash 不同活跃内容行或语言环境的数量。同一内容条目的重复引用和多个源变体只计算一次。回收站中的内容不计入。

数字计数可能会揭示类似草稿的内容。仅在会话用户具有 content:read_drafts 时,或当 API 令牌具有 admin 作用域且其关联用户也具有该权限时才返回。其他媒体读者收到 usage.count: null;这是成功的已编辑响应,不是错误。

每个请求的摘要包含当前所有已注册内容集合的聚合覆盖率:

状态含义
complete每个已注册集合都有当前的、已完成的使用情况覆盖
never没有已注册集合完成过初始使用情况修复
running使用情况修复当前正在运行
partial覆盖率混合或仅注册范围的部分被索引
failed在注册范围内覆盖率失败
stale索引覆盖率已过时
unknown存储的覆盖率包含此版本无法识别的状态

只有 complete 支持在下面描述的 EmDash 管理字段内的有范围的完全零声明。具有任何其他状态的计数是索引投影,可能过度报告或不足报告。即使是完整的结果在并发写入期间也是参考性的;使用情况读取不是事务锁,不得用作删除保证。

获取媒体使用情况详情

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

此端点需要 media:readcontent:read_drafts。令牌认证的调用者还需要 admin 作用域;令牌作用域不会绕过关联用户的权限。

limit 控制每页的内容条目组,范围为 1 到 100(默认:50)。分页永远不会拆分返回条目组的源或出现次数。

{
	"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"
		}
	}
}

授权详情包括活跃和已删除的条目。非空的 deletedAt 标识回收站条目。源为 columnsdraft_overlay;出现次数标识支持的字段和路径,而不暴露内部索引元数据。

媒体使用情况涵盖 EmDash 内容集合管理的顶级图片和文件字段、重复器图片字段和 Portable Text 图片块中的本地媒体引用。它不扫描自定义代码、渲染的 HTML、设置、菜单、小部件、插件私有数据、外部站点或仅提供商的资产。

创建媒体

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

请求体

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

更新媒体

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

请求体

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

删除媒体

DELETE /_emdash/api/media/:id

修复媒体使用情况

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

修复一个集合或所有内容集合的内容媒体使用情况索引。这是管理员/运维端点:会话认证的调用者需要 schema:manage,Bearer 令牌必须具有 admin 作用域,因为路由在 /_emdash/api/admin 下。

全内容修复在当前版本中同步顺序执行。在大型站点上可能代价高昂,因此调用者应有意触发并等待响应。

请求体

修复一个集合:

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

修复所有内容集合:

{
	"scope": "all"
}

请求体是必需的。无效的 slug、未知的请求键、缺少 scope 和无请求体的请求返回 400,而不是默认修复所有内容。

响应

当修复调用产生结构化结果时,端点返回 200。检查 data.statusfailedstale 是修复域状态,不是传输错误。

{
	"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"
			}
		]
	}
}

顶级响应字段:

字段类型描述
statuscomplete | partial | failed | stale聚合修复状态
indexedSourceCountnumber修复期间索引的源
failedSourceCountnumber修复期间失败的源
skippedSourceCountnumber跳过的源,包括过时冲突
deletedSourceCountnumber修复期间删除的过时使用行
collectionsarray每个集合的修复摘要

集合摘要字段:

字段类型描述
collectionstring集合标识符
statuscomplete | partial | failed | stale集合修复状态
indexedSourceCountnumber此集合索引的源
failedSourceCountnumber此集合失败的源
skippedSourceCountnumber此集合跳过的源
deletedSourceCountnumber此集合删除的过时使用行
lastErrorCodestring | null最后的集合修复错误(如果可用)
startedAtstring修复开始时间
completedAtstring | null完成时间,过时结果为 null

未知集合返回 200,带有 data.status: "failed" 和每个集合的 lastErrorCode(如 COLLECTION_NOT_FOUND)。传输错误仍使用标准错误信封,包括 400401403413500

获取媒体文件

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

提供实际的文件内容。仅用于本地存储。

修订版端点

列出修订版

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

参数

参数类型描述
limitnumber返回的最大修订版数(默认:50)

响应

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

获取修订版

GET /_emdash/api/revisions/:revisionId

恢复修订版

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

将内容恢复到此修订版的状态并创建新的修订版。

架构端点

列出集合

GET /_emdash/api/schema/collections

响应

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

获取集合

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

参数

参数类型描述
includeFieldsboolean包含字段定义(查询)

创建集合

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

请求体

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

更新集合

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

删除集合

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

参数

参数类型描述
forceboolean即使集合有内容也删除(查询)

列出字段

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

创建字段

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

请求体

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

更新字段

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

删除字段

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

重新排序字段

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

请求体

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

架构导出

导出架构(JSON)

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

导出架构(TypeScript)

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

返回所有集合的 TypeScript 接口。

插件端点

列出插件

GET /_emdash/api/admin/plugins

获取插件

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

启用插件

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

禁用插件

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

错误代码

代码HTTP 状态描述
NOT_FOUND404资源未找到
VALIDATION_ERROR400无效的输入数据
UNAUTHORIZED401令牌缺失或无效
FORBIDDEN403权限不足
CONTENT_LIST_ERROR500列出内容失败
CONTENT_CREATE_ERROR500创建内容失败
CONTENT_UPDATE_ERROR500更新内容失败
CONTENT_DELETE_ERROR500删除内容失败
MEDIA_LIST_ERROR500列出媒体失败
MEDIA_CREATE_ERROR500创建媒体失败
SCHEMA_CREATE_ERROR500架构操作失败
SLUG_CONFLICT409Slug 已存在
RESERVED_SLUG400Slug 被保留

搜索端点

全局搜索

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

参数

参数类型描述
qstring搜索查询(必填)
collectionsstring逗号分隔的集合标识符
statusstring按状态筛选(默认:published)
limitnumber最大结果数(默认:20)
cursorstring分页游标

响应

{
  "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"
  }
}

搜索建议

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

返回用于自动补全的前缀匹配标题。

重建搜索索引

POST /_emdash/api/search/rebuild

为所有或特定集合重建 FTS 索引。

搜索统计

GET /_emdash/api/search/stats

返回每个集合的索引文档计数。

区块端点

列出区块

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

获取区块

GET /_emdash/api/sections/:slug

创建区块

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

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

更新区块

PUT /_emdash/api/sections/:slug

删除区块

DELETE /_emdash/api/sections/:slug

设置端点

获取所有设置

GET /_emdash/api/settings

更新设置

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

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

菜单端点

列出菜单

GET /_emdash/api/menus

获取菜单

GET /_emdash/api/menus/:name

创建菜单

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

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

更新菜单

PUT /_emdash/api/menus/:name

删除菜单

DELETE /_emdash/api/menus/:name

添加菜单项

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

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

重新排序菜单项

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

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

分类法端点

列出分类法定义

GET /_emdash/api/taxonomies

创建分类法

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

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

列出术语

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

创建术语

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

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

更新术语

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

删除术语

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

设置条目术语

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

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

小部件区域端点

列出小部件区域

GET /_emdash/api/widget-areas

获取小部件区域

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

创建小部件区域

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

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

删除小部件区域

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

添加小部件

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

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

更新小部件

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

删除小部件

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

重新排序小部件

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

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

用户管理端点

列出用户

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

获取用户

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

更新用户

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

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

启用用户

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

禁用用户

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

认证端点

设置状态

GET /_emdash/api/setup/status

返回设置是否完成以及是否存在用户。

Passkey 登录

POST /_emdash/api/auth/passkey/options

获取 WebAuthn 认证选项。

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

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

验证 Passkey 并创建会话。

魔法链接

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

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

登出

POST /_emdash/api/auth/logout

当前用户

GET /_emdash/api/auth/me

邀请用户

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

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

Passkey 管理

GET /_emdash/api/auth/passkey

列出用户的 Passkey。

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

注册新 Passkey。

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

{
  "name": "MacBook Pro"
}

重命名 Passkey。

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

删除 Passkey。

导入端点

分析 WordPress 导出

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

file: <WXR file>

执行 WordPress 导入

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

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

速率限制

API 端点可能根据部署配置进行速率限制。速率限制时,响应包含:

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

CORS

API 支持浏览器请求的 CORS。在部署中配置允许的源。