EmDash 在 /_emdash/api/ 下公开其支持的应用程序编程接口(API)。请使用生成的 OpenAPI 3.1 文档查看请求参数、请求体、响应 schema、状态码以及客户端生成信息:
GET /_emdash/api/openapi.json
该文档由 API 使用的同一套 Zod schema 生成,并反映所配置的最大媒体上传大小。
公开契约边界
OpenAPI 文档是所支持 REST API 的边界。EmDash 还有管理界面与协议工作流的路由。源代码中存在但未出现在 OpenAPI 中的路由,对外部客户端而言不是受支持的 REST 操作。
这一区分适用于备份、署名管理、关系遍历、插件管理、安装、导入与认证等路由。备份请参阅备份指南;受支持的署名管理请使用 MCP 署名工具。OAuth 端点是协议端点,请通过 MCP OAuth 章节 中的元数据发现,而不要将其当作应用 REST 端点。
认证与授权
大多数操作接受 EmDash 会话 cookie 或 Bearer 令牌。在 Authorization 头中发送个人访问令牌或 OAuth 访问令牌:
Authorization: Bearer $EMDASH_TOKEN
Bearer 令牌受其 scope 与关联用户角色限制。会话请求使用用户角色。授权模型请参阅用户角色与令牌 scope。
GET 与 POST /_emdash/api/comments/{collection}/{contentId} 为公开接口。GET 返回已批准评论;POST 提交待审核评论。其余评论审核操作需要认证。
跨站请求伪造防护
通过会话 cookie 认证的状态变更请求需包含此头:
X-EmDash-Request: 1
Bearer 令牌请求不需要该头,因其不使用浏览器隐式凭据。浏览器对公开写操作的请求须发送该头,或 Origin 与 EmDash 站点的公开/请求源一致。
响应封装
成功的 JSON 响应将 success 设为 true,并在 data 中放置操作特定结果:
{
"success": true,
"data": {
"items": []
}
}
错误时 success 为 false,并包含稳定的机器可读代码与消息。部分错误还包含结构化 details:
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Content item not found"
}
}
请使用各 OpenAPI 操作的状态码与错误 schema。常见状态:400 无效输入,401 凭据缺失或无效,403 scope 或权限不足,404 资源不存在,409 状态冲突,413 上传过大,422 插件拒绝保存,500 内部错误。
端点清单
操作 ID 在生成的契约中稳定,OpenAPI 客户端生成器常将其用作方法名。以下清单与生成的 OpenAPI 文档对照校验。
内容
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/content/{collection} | listContent | List content items |
POST | /_emdash/api/content/{collection} | createContent | Create a content item |
GET | /_emdash/api/content/{collection}/{id} | getContent | Get a content item |
PUT | /_emdash/api/content/{collection}/{id} | updateContent | Update a content item |
DELETE | /_emdash/api/content/{collection}/{id} | deleteContent | Delete a content item (soft delete) |
POST | /_emdash/api/content/{collection}/{id}/publish | publishContent | Publish a content item |
POST | /_emdash/api/content/{collection}/{id}/unpublish | unpublishContent | Unpublish a content item |
POST | /_emdash/api/content/{collection}/{id}/schedule | scheduleContent | Schedule content for future publishing |
DELETE | /_emdash/api/content/{collection}/{id}/schedule | unscheduleContent | Cancel scheduled publishing |
POST | /_emdash/api/content/{collection}/{id}/duplicate | duplicateContent | Duplicate a content item |
POST | /_emdash/api/content/{collection}/{id}/restore | restoreContent | Restore a content item from trash |
DELETE | /_emdash/api/content/{collection}/{id}/permanent | permanentDeleteContent | Permanently delete a content item |
GET | /_emdash/api/content/{collection}/{id}/compare | compareContent | Compare live and draft revisions |
POST | /_emdash/api/content/{collection}/{id}/discard-draft | discardDraft | Discard draft changes |
GET | /_emdash/api/content/{collection}/{id}/lock | getEntryLock | Read the entry’s edit lock |
POST | /_emdash/api/content/{collection}/{id}/lock | acquireEntryLock | Take or refresh the entry’s edit lock |
DELETE | /_emdash/api/content/{collection}/{id}/lock | releaseEntryLock | Release the caller’s edit lock |
GET | /_emdash/api/content/{collection}/{id}/translations | getContentTranslations | Get translations for a content item |
GET | /_emdash/api/content/{collection}/{id}/terms/{taxonomy} | getContentTerms | Get taxonomy terms assigned to a content item |
POST | /_emdash/api/content/{collection}/{id}/terms/{taxonomy} | setContentTerms | Set taxonomy terms on a content item |
GET | /_emdash/api/content/{collection}/authors | listContentAuthors | List distinct authors of a collection’s content |
GET | /_emdash/api/content/{collection}/trash | listTrashedContent | List trashed content items |
媒体
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/media | listMedia | List media items |
POST | /_emdash/api/media | uploadMedia | Upload a media item |
GET | /_emdash/api/media/folders | listMediaFolders | List media folders |
POST | /_emdash/api/media/folders | createMediaFolder | Create a media folder |
GET | /_emdash/api/media/folders/{id} | getMediaFolder | Get a media folder |
PUT | /_emdash/api/media/folders/{id} | updateMediaFolder | Update a media folder |
DELETE | /_emdash/api/media/folders/{id} | deleteMediaFolder | Delete a media folder |
GET | /_emdash/api/media/{id} | getMedia | Get a media item |
PUT | /_emdash/api/media/{id} | updateMedia | Update media metadata |
DELETE | /_emdash/api/media/{id} | deleteMedia | Delete a media item |
GET | /_emdash/api/media/{id}/usage | getMediaUsage | Get media usage details |
PUT | /_emdash/api/media/{id}/replace | replaceMediaImage | Replace a media image |
POST | /_emdash/api/admin/media-usage/repair | repairMediaUsage | Repair media usage indexes |
GET | /_emdash/api/admin/media-usage/progress | getMediaUsageProgress | Get media usage indexing progress |
POST | /_emdash/api/admin/media-usage/progress | advanceMediaUsageProgress | Advance media usage indexing |
GET | /_emdash/api/admin/media-usage/work | listMediaUsageWork | List durable media usage work |
GET | /_emdash/api/admin/media-usage/activation | getMediaUsageActivation | Get media usage activation status |
POST | /_emdash/api/admin/media-usage/activation | advanceMediaUsageActivation | Advance media usage activation |
POST | /_emdash/api/admin/media-usage/work/retry | retryMediaUsageWork | Retry one durable media usage job |
GET | /_emdash/api/admin/media-usage/collection-deletions | listMediaUsageCollectionDeletions | List durable collection deletions |
POST | /_emdash/api/admin/media-usage/collection-deletions/retry | retryMediaUsageCollectionDeletion | Retry one collection deletion |
POST | /_emdash/api/media/upload-url | getMediaUploadUrl | Get a media upload target |
POST | /_emdash/api/media/{id}/confirm | confirmMediaUpload | Confirm a media upload |
PUT | /_emdash/api/media/{id}/upload | uploadPendingMedia | Upload a pending media file through EmDash |
Schema
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/schema/block-types | listBlockTypes | List block types |
POST | /_emdash/api/schema/block-types | createBlockType | Create a block type |
GET | /_emdash/api/schema/block-types/{slug} | getBlockType | Get a block type |
PUT | /_emdash/api/schema/block-types/{slug} | updateBlockType | Update a block type |
POST | /_emdash/api/schema/block-types/{slug}/versions/{version}/activate | activateBlockTypeVersion | Activate a block type version |
GET | /_emdash/api/schema/collections | listCollections | List all collections |
POST | /_emdash/api/schema/collections | createCollection | Create a collection |
GET | /_emdash/api/schema/collections/{slug} | getCollection | Get a collection |
PUT | /_emdash/api/schema/collections/{slug} | updateCollection | Update a collection |
DELETE | /_emdash/api/schema/collections/{slug} | deleteCollection | Delete a collection |
GET | /_emdash/api/schema/collections/{slug}/fields | listFields | List fields for a collection |
POST | /_emdash/api/schema/collections/{slug}/fields | createField | Create a field |
GET | /_emdash/api/schema/collections/{slug}/fields/{fieldSlug} | getField | Get a field |
PUT | /_emdash/api/schema/collections/{slug}/fields/{fieldSlug} | updateField | Update a field |
DELETE | /_emdash/api/schema/collections/{slug}/fields/{fieldSlug} | deleteField | Delete a field |
POST | /_emdash/api/schema/collections/reorder | reorderCollections | Reorder collections in the admin sidebar |
POST | /_emdash/api/schema/collections/{slug}/fields/reorder | reorderFields | Reorder fields in a collection |
GET | /_emdash/api/schema/orphans | listOrphanedTables | List orphaned content tables |
POST | /_emdash/api/schema/orphans/{slug} | registerOrphanedTable | Register an orphaned table as a collection |
评论
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/comments/{collection}/{contentId} | listPublicComments | List approved comments for content |
POST | /_emdash/api/comments/{collection}/{contentId} | createComment | Submit a new comment |
GET | /_emdash/api/admin/comments | listAdminComments | List comments for moderation |
GET | /_emdash/api/admin/comments/counts | getCommentCounts | Get comment status counts |
POST | /_emdash/api/admin/comments/bulk | bulkCommentAction | Bulk approve, spam, trash, or delete comments |
GET | /_emdash/api/admin/comments/{id} | getComment | Get a single comment |
DELETE | /_emdash/api/admin/comments/{id} | deleteComment | Permanently delete a comment |
PUT | /_emdash/api/admin/comments/{id}/status | updateCommentStatus | Change comment status |
分类法
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/taxonomies | listTaxonomies | List all taxonomy definitions |
GET | /_emdash/api/taxonomies/{name} | getTaxonomy | Get a taxonomy definition |
PUT | /_emdash/api/taxonomies/{name} | updateTaxonomy | Update a taxonomy definition |
DELETE | /_emdash/api/taxonomies/{name} | deleteTaxonomy | Delete a taxonomy, its terms, and their content assignments |
GET | /_emdash/api/taxonomies/{name}/translations | listTaxonomyTranslations | List every locale variant of a taxonomy definition |
POST | /_emdash/api/taxonomies/{name}/reorder | reorderTerms | Set the manual order of one sibling group of terms |
GET | /_emdash/api/taxonomies/{name}/terms | listTerms | List terms for a taxonomy |
POST | /_emdash/api/taxonomies/{name}/terms | createTerm | Create a term |
GET | /_emdash/api/taxonomies/{name}/terms/{slug} | getTerm | Get a term by slug |
PUT | /_emdash/api/taxonomies/{name}/terms/{slug} | updateTerm | Update a term |
DELETE | /_emdash/api/taxonomies/{name}/terms/{slug} | deleteTerm | Delete a term |
菜单
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/menus | listMenus | List all menus with item counts |
POST | /_emdash/api/menus | createMenu | Create a menu |
GET | /_emdash/api/menus/{name} | getMenu | Get a menu with all items |
PUT | /_emdash/api/menus/{name} | updateMenu | Update a menu |
DELETE | /_emdash/api/menus/{name} | deleteMenu | Delete a menu and its items |
POST | /_emdash/api/menus/{name}/items | createMenuItem | Add an item to a menu |
PUT | /_emdash/api/menus/{name}/items/{id} | updateMenuItem | Update a menu item |
DELETE | /_emdash/api/menus/{name}/items/{id} | deleteMenuItem | Delete a menu item |
POST | /_emdash/api/menus/{name}/reorder | reorderMenuItems | Batch reorder menu items |
分区
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/sections | listSections | List sections |
POST | /_emdash/api/sections | createSection | Create a section |
GET | /_emdash/api/sections/{slug} | getSection | Get a section by slug |
PUT | /_emdash/api/sections/{slug} | updateSection | Update a section |
DELETE | /_emdash/api/sections/{slug} | deleteSection | Delete a section |
小部件
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/widget-areas | listWidgetAreas | List all widget areas |
POST | /_emdash/api/widget-areas | createWidgetArea | Create a widget area |
GET | /_emdash/api/widget-areas/{name} | getWidgetArea | Get a widget area with widgets |
DELETE | /_emdash/api/widget-areas/{name} | deleteWidgetArea | Delete a widget area and its widgets |
POST | /_emdash/api/widget-areas/{name}/widgets | createWidget | Add a widget to an area |
PUT | /_emdash/api/widget-areas/{name}/widgets/{id} | updateWidget | Update a widget |
DELETE | /_emdash/api/widget-areas/{name}/widgets/{id} | deleteWidget | Delete a widget |
POST | /_emdash/api/widget-areas/{name}/reorder | reorderWidgets | Reorder widgets in an area |
设置
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/settings | getSettings | Get site settings |
PUT | /_emdash/api/settings | updateSettings | Update site settings |
搜索
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/search | search | Full-text search across collections |
GET | /_emdash/api/search/suggest | searchSuggest | Autocomplete search suggestions |
POST | /_emdash/api/search/rebuild | rebuildSearchIndex | Rebuild the search index for a collection |
POST | /_emdash/api/search/enable | enableSearch | Enable or disable search for a collection |
GET | /_emdash/api/search/stats | getSearchStats | Get search index statistics |
重定向
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/redirects | listRedirects | List redirects |
POST | /_emdash/api/redirects | createRedirect | Create a redirect rule |
GET | /_emdash/api/redirects/{id} | getRedirect | Get a redirect |
PUT | /_emdash/api/redirects/{id} | updateRedirect | Update a redirect |
DELETE | /_emdash/api/redirects/{id} | deleteRedirect | Delete a redirect |
GET | /_emdash/api/redirects/404s | listNotFoundEntries | List 404 log entries |
POST | /_emdash/api/redirects/404s | pruneNotFoundLog | Prune old 404 log entries |
DELETE | /_emdash/api/redirects/404s | clearNotFoundLog | Clear all 404 log entries |
GET | /_emdash/api/redirects/404s/summary | getNotFoundSummary | Get 404 summary grouped by path |
用户
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/admin/users | listUsers | List users |
GET | /_emdash/api/admin/users/{id} | getUser | Get user details |
PUT | /_emdash/api/admin/users/{id} | updateUser | Update a user |
POST | /_emdash/api/admin/users/{id}/disable | disableUser | Disable a user account |
POST | /_emdash/api/admin/users/{id}/enable | enableUser | Enable a user account |
GET | /_emdash/api/admin/allowed-domains | listAllowedDomains | List allowed email domains |
POST | /_emdash/api/admin/allowed-domains | createAllowedDomain | Add an allowed email domain |
PUT | /_emdash/api/admin/allowed-domains/{domain} | updateAllowedDomain | Update an allowed domain |
DELETE | /_emdash/api/admin/allowed-domains/{domain} | deleteAllowedDomain | Remove an allowed domain |
站点迁移
| 方法 | 路径 | 操作 | 摘要 |
|---|---|---|---|
GET | /_emdash/api/admin/transfer/capabilities | getTransferCapabilities | Get site transfer capabilities |
GET | /_emdash/api/admin/transfer/imports | listTransferImports | List site imports |
POST | /_emdash/api/admin/transfer/imports | createTransferImport | Create a site import |
GET | /_emdash/api/admin/transfer/imports/{id} | getTransferImport | Get a site import |
GET | /_emdash/api/admin/transfer/imports/{id}/missing | listTransferImportMissingFiles | List package files still to upload |
PUT | /_emdash/api/admin/transfer/imports/{id}/files/{path} | uploadTransferImportFile | Upload one package file |
POST | /_emdash/api/admin/transfer/imports/{id}/analyze | analyzeTransferImport | Advance import analysis |
GET | /_emdash/api/admin/transfer/imports/{id}/plan | getTransferImportPlan | Get an import plan |
POST | /_emdash/api/admin/transfer/imports/{id}/cancel | cancelTransferImport | Cancel a site import |
POST | /_emdash/api/admin/transfer/imports/{id}/abandon | abandonTransferImport | Abandon a failed or cancelled import |
POST | /_emdash/api/admin/transfer/imports/{id}/execute | executeTransferImport | Start a planned import |
POST | /_emdash/api/admin/transfer/imports/{id}/advance | advanceTransferImport | Advance an executing import |
GET | /_emdash/api/admin/transfer/imports/{id}/receipt | getTransferImportReceipt | Get an import receipt |
GET | /_emdash/api/admin/transfer/exports | listTransferExports | List site exports |
POST | /_emdash/api/admin/transfer/exports | createTransferExport | Start a site export |
GET | /_emdash/api/admin/transfer/exports/{id} | getTransferExport | Get a site export |
POST | /_emdash/api/admin/transfer/exports/{id}/advance | advanceTransferExport | Advance a site export |
GET | /_emdash/api/admin/transfer/exports/{id}/manifest | getTransferExportManifest | Download an export manifest |
GET | /_emdash/api/admin/transfer/exports/{id}/files/{path} | downloadTransferExportFile | Download one export file |
GET | /_emdash/api/admin/transfer/exports/{id}/archive | downloadTransferExportArchive | Download an export archive |
GET | /_emdash/api/admin/transfer/approvals | listTransferApprovals | List transfer approvals |
POST | /_emdash/api/admin/transfer/approvals/{id}/approve | approveTransferApproval | Approve a transfer request |
POST | /_emdash/api/admin/transfer/approvals/{id}/deny | denyTransferApproval | Deny a transfer request |
内容生命周期与署名
内容生命周期参考 定义了 REST、MCP、CLI 与管理后台共享的状态、修订、权限、冲突与钩子行为。
内容读取在可用时会返回不透明的 _rev 令牌。在 PUT /content/{collection}/{id} 时附带 _rev,以免覆盖自读取以来发生的变更。过期的令牌会产生冲突;重试前请再次读取条目。CLI 对 content update 强制此检查,而 REST 字段对有意选择无条件写入的客户端仍为可选。
读取并更新条目
修改前先读取条目:
GET /_emdash/api/content/articles/01JARTICLE0000000000000000
Authorization: Bearer $EMDASH_TOKEN
响应包含其字段、发布状态与修订令牌:
{
"success": true,
"data": {
"item": {
"id": "01JARTICLE0000000000000000",
"type": "articles",
"slug": "launch-notes",
"status": "published",
"data": { "title": "Launch notes" }
},
"_rev": "opaque-revision-token"
}
}
仅发送需要变更的字段,以及该次读取得到的令牌:
PUT /_emdash/api/content/articles/01JARTICLE0000000000000000
Authorization: Bearer $EMDASH_TOKEN
Content-Type: application/json
{
"data": { "title": "Updated launch notes" },
"_rev": "opaque-revision-token"
}
修改已发布条目会创建草稿,先前版本仍保持线上。调用 compare 操作查看两个版本,然后发布草稿或丢弃。取消发布会保留内容及其发布日期、取消任何待执行计划,并将条目从线上站点移除。
创建与更新请求体可接受署名 credits,内容响应包含主署名与有序 credits。内容列表可按存储的署名 ID 筛选,并可选择包含作者推断的署名。署名记录本身的创建与管理通过 MCP 署名工具 提供,不在公开 REST 契约内。
生命周期操作区分软删除与永久删除。还原会将回收站内容恢复为无计划的草稿;永久删除会移除回收站中的条目且不可撤销。发布、取消发布、计划、取消计划、比较、丢弃草稿与复制是独立操作,客户端可一次请求一种状态转换。
条目编辑锁
编辑者打开条目时,集合可获取七分钟编辑锁。使用 /content/{collection}/{id}/lock 上的三个操作来读取、获取或刷新以及释放租约。
读取或获取响应会显示是否启用锁定、调用方是否持有租约以及当前持有者:
{
"success": true,
"data": {
"enabled": true,
"heldByCaller": false,
"holder": {
"userId": "01JUSER000000000000000000",
"userName": "Ada",
"acquiredAt": "2026-05-01T09:12:04.117Z",
"expiresAt": "2026-05-01T09:19:04.117Z"
}
}
}
集合禁用编辑锁时,enabled 为 false 且不会占用租约。再次获取同一把锁并保存条目都会延长调用方持有的租约。
获取请求体可包含标识单次编辑会话的不透明 token,以及用户选择取代其他编辑者租约时的 takeover: true。释放锁时将同一 token 作为查询参数传入。同一账户的第二个标签页便无法误释放第一个标签页的租约。
当其他用户持有租约时,受保护的内容写入返回 409 ENTRY_LOCKED。错误 details 标明持有者与过期时间。要覆盖锁,在有请求体的写入 JSON 中发送 "overrideLock": true,或对无请求体的 DELETE 操作使用 ?overrideLock=true。
引用选择
reference 字段 通过关系将条目链接到另一集合中的条目。其值不属于 data,且按翻译组键控,因此条目的各语言翻译共享同一选择。
创建与更新请求体在 references 下携带选择,按字段 slug 键控,每项为最多 1000 个条目 ID 的数组,顺序为展示顺序。EmDash 在与条目同一事务中写入选择。以下更新替换条目的作者:
PUT /_emdash/api/content/articles/01JARTICLE0000000000000000
Authorization: Bearer $EMDASH_TOKEN
Content-Type: application/json
{
"data": { "title": "Updated launch notes" },
"references": { "author": ["01JAUTHOR00000000000000000"] },
"_rev": "opaque-revision-token"
}
绑定到关系子端的字段会选择指向当前写入条目的条目,这些条目本身没有顺序。关系两端都会强制执行限制,因此若选择会使链接条目拥有超过关系允许的上级数量,会与链接过多条目一样被拒绝。
在保留修订的集合上,已发布条目上变更的选择会与条目其他待处理编辑一起暂存于草稿。条目发布时生效,随草稿丢弃。发布会针对关系限制重新检查整个选择。
单条目读取返回按字段 slug 键控的 references。每个字段包含其链接条目的第一页(50 条),若还有更多则带 nextCursor,并报告各条目的 ID、slug、集合、展示标题、解析后的 locale 与翻译组。可读取草稿的调用方会看到草稿中暂存的选择。内容列表操作不包含 references。
关系定义与链接遍历是管理路由,不在 OpenAPI 中,也不属于公开契约。通过上述内容操作写入与读取选择,并在站点上使用 getEmDashEntry() 与 getEmDashReferences() 渲染。
翻译
公开 REST 契约暴露内容翻译与分类法定义翻译。内容创建接受 translationOf,分类法创建使用同一字段添加 locale 变体。内容-术语操作返回感知 locale 的分配。
省略 locale 时,GET /taxonomies/{name} 返回站点默认 locale 的定义;仅当默认 locale 无定义时才回退到最低 locale 代码。更新行为不同:省略 locale 时会修改最低 locale 代码的定义。请传入 locale,使翻译分类法的更新作用于目标定义。若该 locale 无定义,更新返回 NOT_FOUND 而非回退到其他 locale。translations 操作返回共享组中的全部定义,并提供 translationOf 接受的 ID。
label 与 labelSingular 属于某一 locale 的定义。hierarchical 与 collections 属于分类法:各 locale 返回相同值,更新中发送任一项会改变所有 locale。为已在其他 locale 存在的名称创建定义会加入该分类法,无论请求是否发送 translationOf。新定义采用分类法的 hierarchical 与 collections;创建时发送不同值会返回 VALIDATION_ERROR。
删除分类法会移除其定义的所有 locale、全部术语以及这些术语对内容的全部分配,不会删除内容条目本身。
术语重排操作改变一个兄弟组。ids 数组可只包含该组的一部分;列出的术语交换现有位置,未列出的术语保持原位。例如对 [A, B, C] 用 ids: ["C", "A"] 重排得到 [C, B, A]。重排不改变父子关系,同一术语顺序适用于其翻译组中的各 locale。
菜单、分类法术语与署名翻译路由不在公开 REST 契约内。其受支持的翻译列表可通过 MCP 服务器 上的 menu_translations、taxonomy_term_translations 与 byline_translations 获取。
媒体端点
媒体操作涵盖列表、上传、元数据更新、图片替换、文件夹、使用信息与使用索引维护。媒体库指南 说明面向用户的工作流及使用覆盖的含义。
列出与查看媒体
GET /media 支持游标或页码分页、MIME 类型与文件名筛选、文件夹以及可选的使用摘要。省略 folderId 包含所有文件夹,或传入 folderId=unfiled 仅返回主库。列表或单项读取时设置 includeUsage=1 以包含使用信息;其他值无效。
usage.count 统计当前索引源引用该媒体项的不同活跃内容行或 locale,以及选择它的各站点设置(logo、favicon、seo.defaultOgImage)。同一条目内重复引用只计一次,回收站条目不计入。仅可读取草稿的调用方可见该数字;其他已授权媒体读者收到 count: null,因为计数可能泄露草稿内容。
GET /media/{id}/usage 分页返回引用该媒体的内容条目。每页还包含 siteSettings(选择该媒体项的站点设置),例如 [{ "setting": "favicon" }]。站点设置每次请求从存储的设置读取,不依赖使用索引。
每个使用结果包含覆盖状态:
| 状态 | 含义 |
|---|---|
complete | 每个已注册集合都有当前使用覆盖。 |
never | 没有已注册集合完成初始使用修复。 |
running | 修复进行中。 |
partial | 仅部分已注册集合有当前覆盖。 |
failed | 已注册集合集上的覆盖失败。 |
stale | 索引早于其所描述的内容。 |
unknown | 存储状态不被此 EmDash 版本识别。 |
仅 complete 支持在已索引字段类型内将零计数视为完整。并发写入期间计数仅供参考;不会锁定媒体项或保证删除安全。使用索引覆盖图片与文件字段、Repeater 图片字段、Portable Text 图片与图库块,以及 EmDash 集合中保留块版本声明的媒体。站点 logo、favicon 与默认社交图设置也会报告。使用不包括自定义 Portable Text 块、应用代码、渲染 HTML、其他设置、菜单、小部件、插件数据、外部站点或仅提供商资产。
直接 multipart 上传
通过 multipart 请求将文件作为 file 字段 POST 到 EmDash:
curl --request POST \
--header "Authorization: Bearer $EMDASH_TOKEN" \
--form "file=@./cover.jpg;type=image/jpeg" \
https://example.com/_emdash/api/media
curl 会自动添加 multipart 边界。请勿手动设置 Content-Type 头。OpenAPI MediaDirectUploadBody schema 列出可选元数据字段,响应 schema 区分新上传与去重后的现有项。
multipart 请求体还可包含图片 width、height、应应用 MIME 类型 allowlist 的 fieldId,以及用于创建低质量占位符的缩小 thumbnail。新文件返回 201 Created 并立即可用。相同字节返回现有媒体项,200 OK 且 deduplicated: true。
上传目标流程
当客户端可直接上传到 S3 兼容存储时使用上传目标流程。媒体项保持 pending,确认成功前不会出现在标准库中。
-
请求上传目标
POST /_emdash/api/media/upload-url Authorization: Bearer $EMDASH_TOKEN Content-Type: application/json { "filename": "cover.jpg", "contentType": "image/jpeg", "size": 102400 }响应提供
uploadUrl、method、headers、mediaId、storageKey与过期时间。当contentHash与相同 MIME 类型与大小的现有文件匹配时,响应改为设置existing: true;使用该媒体项,勿再上传或确认另一份副本。 -
上传字节
使用返回的 method 与 headers。将根相对 URL 解析为 EmDash 站点并包含 Bearer 令牌。向其他源的绝对 URL 仅发送返回的上传 headers。
-
确认上传
POST /_emdash/api/media/01JMEDIA000000000000000000/confirm Authorization: Bearer $EMDASH_TOKEN Content-Type: application/json { "size": 102400, "width": 1920, "height": 1080 }确认会校验存储对象并将项从
pending变为ready。提供的 size 与尺寸必须与上传文件一致。
本地存储与原生 R2 返回同源的 EmDash 上传目标。S3 兼容存储可能返回签名的外部 URL。pending 项在确认成功前不会出现在标准媒体列表中。
上传错误
以下错误需要不同的恢复操作:
| 状态 | 代码 | 操作 |
|---|---|---|
400 | NO_FILE | 在 multipart 请求中添加 file 字段,或发送缺失的上传体。 |
400 | INVALID_TYPE | 使用与 pending 媒体项匹配的允许 MIME 类型。 |
400 | VALIDATION_ERROR | 修正缺失或无效的元数据,包括超过配置大小限制的值。 |
400 | FILE_NOT_FOUND | 确认前先将对象上传到返回的目标。 |
400 | UPLOAD_SIZE_MISMATCH | 以正确 size 重新开始流程;声明、上传与确认的 size 必须一致。 |
400 或 409 | INVALID_STATE | 重试前读取媒体项。它可能已非 pending,或在确认期间被其他请求更改。 |
404 | NOT_FOUND | 使用现有的 pending 媒体 ID。 |
413 | PAYLOAD_TOO_LARGE | 减小文件大小或增大 maxUploadSize 后再开始上传。 |
媒体文件夹
文件夹名称会 trim、限制 200 字符,并在 Unicode 规范化与小写后比较。因此 Photos、photos 与 PHOTOS 等名称会冲突。删除文件夹会将其媒体移回主库;不会删除媒体、更改媒体 ID 或 URL,或更改使用记录。
修复媒体使用
媒体使用激活、进度、工作队列、删除清理与修复是 /_emdash/api/admin/media-usage/ 下的运维操作。会话用户需要 schema:manage;Bearer 令牌还需要 admin scope。
跟踪关闭时,激活前暂停直接写数据库的进程。EmDash 在设置运行期间会暂时阻止通过其 API 发送的内容与 schema 写入,但无法阻止直接写数据库的其他进程。
- 停止直接写数据库的进程,等待进行中的写入完成。
- 读取激活状态。
expanded表示跟踪关闭,activating表示 EmDash 正在准备集合,active表示新的媒体引用变更会被跟踪。 - 发送一次激活请求
{ "writersDrained": true }。 - 逐次发送进度请求,等待每次返回的
nextRequestInMs,直到激活为active。 - 恢复直接写数据库。
- 继续进度请求,直到历史索引为
ready且nextRequestInMs为null。
若写入请求超时或返回 409 或 500,重试前读取激活与进度状态。已完成的批次仍会记录。当设置 lastErrorCode 时,保持直接写入停止,解决报告的问题,并发送一次确认的重试。激活开始后无法取消或重置,请在 staging 副本上测试此流程并保持当前数据库备份。
工作列表操作暴露失败或延迟的条目索引,不返回内容、媒体引用、租约令牌、原始数据库错误或精确积压计数。重试单项是幂等的。409 WORK_LEASE_ACTIVE 表示 worker 仍在处理该项;响应包含 details.leaseExpiresAt,请等到该时间并再次读取项后再重试。409 WORK_CHANGED 表示其他请求更改了工作项;读取其当前状态,勿覆盖较新的工作。
修复操作接受 { "scope": "collection", "collection": "articles" } 或 { "scope": "all" }。全集合修复同步且顺序执行,大型站点可能耗时很长。200 响应仍可能报告 partial、failed 或 stale;在视为修复完成前检查 data.status、各集合状态与源计数。
站点迁移
/_emdash/api/admin/transfer/ 下的迁移操作将站点导出为站点包并导入到空站点。站点迁移指南 描述导出、上传、分析、执行与回执工作流。
会话用户需要 transfer:export 或 transfer:import 权限,仅管理员持有。Bearer 令牌需要 admin 或各操作指定的 transfer:export、transfer:analyze 或 transfer:execute scope。审批操作仅接受已登录会话。它们裁决 MCP site_export_start 与 site_import_start 工具发起的请求;REST 导出与执行操作不需要审批。
导入执行期间,以及失败或取消后直至放弃之前,大多数其他写入操作返回 503 TRANSFER_IMPORT_IN_PROGRESS。指南列出导入期间仍可用的操作。
分页
列表操作在 OpenAPI 中描述其分页参数。大多数游标分页操作接受不透明 cursor 与 1 至 100 的 limit,默认 50。原样返回上一响应的 nextCursor;请勿解析或构造它。部分媒体操作还支持页码,部分专用列表使用不同 limit,生成的客户端应遵循各操作的 schema。
搜索分词器
search-enable 操作为每个集合存储分词器。在已启用搜索的集合上更改它会重建该集合索引。
| 值 | 用途 |
|---|---|
porter unicode61 | 适合 Porter 词干提取的英文内容的默认值。 |
unicode61 | 使用词分隔符但不应使用英文词干提取的语言。 |
trigram | 无空格文本(含日文、中文、泰文、高棉文、老挝文、缅甸文)或需要子串匹配的集合。少于三个 Unicode 字符的查询无匹配。 |
禁用搜索会保留存储的分词器供下次启用。rebuild 操作使用集合存储的分词器与字段权重。
评论与重定向
公开评论提交进入审核队列。管理评论操作列出所有状态、返回计数、更新单一状态、批量审核并永久删除评论。429 响应表示达到提交速率限制。
重定向操作分别管理重定向规则与记录的 404 日志。修剪按请求体所选条目移除,而 DELETE /redirects/404s 清空整个日志。两者均不删除重定向规则。