REST API 参考

本页内容

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}listContentList content items
POST/_emdash/api/content/{collection}createContentCreate a content item
GET/_emdash/api/content/{collection}/{id}getContentGet a content item
PUT/_emdash/api/content/{collection}/{id}updateContentUpdate a content item
DELETE/_emdash/api/content/{collection}/{id}deleteContentDelete a content item (soft delete)
POST/_emdash/api/content/{collection}/{id}/publishpublishContentPublish a content item
POST/_emdash/api/content/{collection}/{id}/unpublishunpublishContentUnpublish a content item
POST/_emdash/api/content/{collection}/{id}/schedulescheduleContentSchedule content for future publishing
DELETE/_emdash/api/content/{collection}/{id}/scheduleunscheduleContentCancel scheduled publishing
POST/_emdash/api/content/{collection}/{id}/duplicateduplicateContentDuplicate a content item
POST/_emdash/api/content/{collection}/{id}/restorerestoreContentRestore a content item from trash
DELETE/_emdash/api/content/{collection}/{id}/permanentpermanentDeleteContentPermanently delete a content item
GET/_emdash/api/content/{collection}/{id}/comparecompareContentCompare live and draft revisions
POST/_emdash/api/content/{collection}/{id}/discard-draftdiscardDraftDiscard draft changes
GET/_emdash/api/content/{collection}/{id}/lockgetEntryLockRead the entry’s edit lock
POST/_emdash/api/content/{collection}/{id}/lockacquireEntryLockTake or refresh the entry’s edit lock
DELETE/_emdash/api/content/{collection}/{id}/lockreleaseEntryLockRelease the caller’s edit lock
GET/_emdash/api/content/{collection}/{id}/translationsgetContentTranslationsGet translations for a content item
GET/_emdash/api/content/{collection}/{id}/terms/{taxonomy}getContentTermsGet taxonomy terms assigned to a content item
POST/_emdash/api/content/{collection}/{id}/terms/{taxonomy}setContentTermsSet taxonomy terms on a content item
GET/_emdash/api/content/{collection}/authorslistContentAuthorsList distinct authors of a collection’s content
GET/_emdash/api/content/{collection}/trashlistTrashedContentList trashed content items

媒体

方法路径操作摘要
GET/_emdash/api/medialistMediaList media items
POST/_emdash/api/mediauploadMediaUpload a media item
GET/_emdash/api/media/folderslistMediaFoldersList media folders
POST/_emdash/api/media/folderscreateMediaFolderCreate a media folder
GET/_emdash/api/media/folders/{id}getMediaFolderGet a media folder
PUT/_emdash/api/media/folders/{id}updateMediaFolderUpdate a media folder
DELETE/_emdash/api/media/folders/{id}deleteMediaFolderDelete a media folder
GET/_emdash/api/media/{id}getMediaGet a media item
PUT/_emdash/api/media/{id}updateMediaUpdate media metadata
DELETE/_emdash/api/media/{id}deleteMediaDelete a media item
GET/_emdash/api/media/{id}/usagegetMediaUsageGet media usage details
PUT/_emdash/api/media/{id}/replacereplaceMediaImageReplace a media image
POST/_emdash/api/admin/media-usage/repairrepairMediaUsageRepair media usage indexes
GET/_emdash/api/admin/media-usage/progressgetMediaUsageProgressGet media usage indexing progress
POST/_emdash/api/admin/media-usage/progressadvanceMediaUsageProgressAdvance media usage indexing
GET/_emdash/api/admin/media-usage/worklistMediaUsageWorkList durable media usage work
GET/_emdash/api/admin/media-usage/activationgetMediaUsageActivationGet media usage activation status
POST/_emdash/api/admin/media-usage/activationadvanceMediaUsageActivationAdvance media usage activation
POST/_emdash/api/admin/media-usage/work/retryretryMediaUsageWorkRetry one durable media usage job
GET/_emdash/api/admin/media-usage/collection-deletionslistMediaUsageCollectionDeletionsList durable collection deletions
POST/_emdash/api/admin/media-usage/collection-deletions/retryretryMediaUsageCollectionDeletionRetry one collection deletion
POST/_emdash/api/media/upload-urlgetMediaUploadUrlGet a media upload target
POST/_emdash/api/media/{id}/confirmconfirmMediaUploadConfirm a media upload
PUT/_emdash/api/media/{id}/uploaduploadPendingMediaUpload a pending media file through EmDash

Schema

方法路径操作摘要
GET/_emdash/api/schema/block-typeslistBlockTypesList block types
POST/_emdash/api/schema/block-typescreateBlockTypeCreate a block type
GET/_emdash/api/schema/block-types/{slug}getBlockTypeGet a block type
PUT/_emdash/api/schema/block-types/{slug}updateBlockTypeUpdate a block type
POST/_emdash/api/schema/block-types/{slug}/versions/{version}/activateactivateBlockTypeVersionActivate a block type version
GET/_emdash/api/schema/collectionslistCollectionsList all collections
POST/_emdash/api/schema/collectionscreateCollectionCreate a collection
GET/_emdash/api/schema/collections/{slug}getCollectionGet a collection
PUT/_emdash/api/schema/collections/{slug}updateCollectionUpdate a collection
DELETE/_emdash/api/schema/collections/{slug}deleteCollectionDelete a collection
GET/_emdash/api/schema/collections/{slug}/fieldslistFieldsList fields for a collection
POST/_emdash/api/schema/collections/{slug}/fieldscreateFieldCreate a field
GET/_emdash/api/schema/collections/{slug}/fields/{fieldSlug}getFieldGet a field
PUT/_emdash/api/schema/collections/{slug}/fields/{fieldSlug}updateFieldUpdate a field
DELETE/_emdash/api/schema/collections/{slug}/fields/{fieldSlug}deleteFieldDelete a field
POST/_emdash/api/schema/collections/reorderreorderCollectionsReorder collections in the admin sidebar
POST/_emdash/api/schema/collections/{slug}/fields/reorderreorderFieldsReorder fields in a collection
GET/_emdash/api/schema/orphanslistOrphanedTablesList orphaned content tables
POST/_emdash/api/schema/orphans/{slug}registerOrphanedTableRegister an orphaned table as a collection

评论

方法路径操作摘要
GET/_emdash/api/comments/{collection}/{contentId}listPublicCommentsList approved comments for content
POST/_emdash/api/comments/{collection}/{contentId}createCommentSubmit a new comment
GET/_emdash/api/admin/commentslistAdminCommentsList comments for moderation
GET/_emdash/api/admin/comments/countsgetCommentCountsGet comment status counts
POST/_emdash/api/admin/comments/bulkbulkCommentActionBulk approve, spam, trash, or delete comments
GET/_emdash/api/admin/comments/{id}getCommentGet a single comment
DELETE/_emdash/api/admin/comments/{id}deleteCommentPermanently delete a comment
PUT/_emdash/api/admin/comments/{id}/statusupdateCommentStatusChange comment status

分类法

方法路径操作摘要
GET/_emdash/api/taxonomieslistTaxonomiesList all taxonomy definitions
GET/_emdash/api/taxonomies/{name}getTaxonomyGet a taxonomy definition
PUT/_emdash/api/taxonomies/{name}updateTaxonomyUpdate a taxonomy definition
DELETE/_emdash/api/taxonomies/{name}deleteTaxonomyDelete a taxonomy, its terms, and their content assignments
GET/_emdash/api/taxonomies/{name}/translationslistTaxonomyTranslationsList every locale variant of a taxonomy definition
POST/_emdash/api/taxonomies/{name}/reorderreorderTermsSet the manual order of one sibling group of terms
GET/_emdash/api/taxonomies/{name}/termslistTermsList terms for a taxonomy
POST/_emdash/api/taxonomies/{name}/termscreateTermCreate a term
GET/_emdash/api/taxonomies/{name}/terms/{slug}getTermGet a term by slug
PUT/_emdash/api/taxonomies/{name}/terms/{slug}updateTermUpdate a term
DELETE/_emdash/api/taxonomies/{name}/terms/{slug}deleteTermDelete a term

菜单

方法路径操作摘要
GET/_emdash/api/menuslistMenusList all menus with item counts
POST/_emdash/api/menuscreateMenuCreate a menu
GET/_emdash/api/menus/{name}getMenuGet a menu with all items
PUT/_emdash/api/menus/{name}updateMenuUpdate a menu
DELETE/_emdash/api/menus/{name}deleteMenuDelete a menu and its items
POST/_emdash/api/menus/{name}/itemscreateMenuItemAdd an item to a menu
PUT/_emdash/api/menus/{name}/items/{id}updateMenuItemUpdate a menu item
DELETE/_emdash/api/menus/{name}/items/{id}deleteMenuItemDelete a menu item
POST/_emdash/api/menus/{name}/reorderreorderMenuItemsBatch reorder menu items

分区

方法路径操作摘要
GET/_emdash/api/sectionslistSectionsList sections
POST/_emdash/api/sectionscreateSectionCreate a section
GET/_emdash/api/sections/{slug}getSectionGet a section by slug
PUT/_emdash/api/sections/{slug}updateSectionUpdate a section
DELETE/_emdash/api/sections/{slug}deleteSectionDelete a section

小部件

方法路径操作摘要
GET/_emdash/api/widget-areaslistWidgetAreasList all widget areas
POST/_emdash/api/widget-areascreateWidgetAreaCreate a widget area
GET/_emdash/api/widget-areas/{name}getWidgetAreaGet a widget area with widgets
DELETE/_emdash/api/widget-areas/{name}deleteWidgetAreaDelete a widget area and its widgets
POST/_emdash/api/widget-areas/{name}/widgetscreateWidgetAdd a widget to an area
PUT/_emdash/api/widget-areas/{name}/widgets/{id}updateWidgetUpdate a widget
DELETE/_emdash/api/widget-areas/{name}/widgets/{id}deleteWidgetDelete a widget
POST/_emdash/api/widget-areas/{name}/reorderreorderWidgetsReorder widgets in an area

设置

方法路径操作摘要
GET/_emdash/api/settingsgetSettingsGet site settings
PUT/_emdash/api/settingsupdateSettingsUpdate site settings

搜索

方法路径操作摘要
GET/_emdash/api/searchsearchFull-text search across collections
GET/_emdash/api/search/suggestsearchSuggestAutocomplete search suggestions
POST/_emdash/api/search/rebuildrebuildSearchIndexRebuild the search index for a collection
POST/_emdash/api/search/enableenableSearchEnable or disable search for a collection
GET/_emdash/api/search/statsgetSearchStatsGet search index statistics

重定向

方法路径操作摘要
GET/_emdash/api/redirectslistRedirectsList redirects
POST/_emdash/api/redirectscreateRedirectCreate a redirect rule
GET/_emdash/api/redirects/{id}getRedirectGet a redirect
PUT/_emdash/api/redirects/{id}updateRedirectUpdate a redirect
DELETE/_emdash/api/redirects/{id}deleteRedirectDelete a redirect
GET/_emdash/api/redirects/404slistNotFoundEntriesList 404 log entries
POST/_emdash/api/redirects/404spruneNotFoundLogPrune old 404 log entries
DELETE/_emdash/api/redirects/404sclearNotFoundLogClear all 404 log entries
GET/_emdash/api/redirects/404s/summarygetNotFoundSummaryGet 404 summary grouped by path

用户

方法路径操作摘要
GET/_emdash/api/admin/userslistUsersList users
GET/_emdash/api/admin/users/{id}getUserGet user details
PUT/_emdash/api/admin/users/{id}updateUserUpdate a user
POST/_emdash/api/admin/users/{id}/disabledisableUserDisable a user account
POST/_emdash/api/admin/users/{id}/enableenableUserEnable a user account
GET/_emdash/api/admin/allowed-domainslistAllowedDomainsList allowed email domains
POST/_emdash/api/admin/allowed-domainscreateAllowedDomainAdd an allowed email domain
PUT/_emdash/api/admin/allowed-domains/{domain}updateAllowedDomainUpdate an allowed domain
DELETE/_emdash/api/admin/allowed-domains/{domain}deleteAllowedDomainRemove an allowed domain

站点迁移

方法路径操作摘要
GET/_emdash/api/admin/transfer/capabilitiesgetTransferCapabilitiesGet site transfer capabilities
GET/_emdash/api/admin/transfer/importslistTransferImportsList site imports
POST/_emdash/api/admin/transfer/importscreateTransferImportCreate a site import
GET/_emdash/api/admin/transfer/imports/{id}getTransferImportGet a site import
GET/_emdash/api/admin/transfer/imports/{id}/missinglistTransferImportMissingFilesList package files still to upload
PUT/_emdash/api/admin/transfer/imports/{id}/files/{path}uploadTransferImportFileUpload one package file
POST/_emdash/api/admin/transfer/imports/{id}/analyzeanalyzeTransferImportAdvance import analysis
GET/_emdash/api/admin/transfer/imports/{id}/plangetTransferImportPlanGet an import plan
POST/_emdash/api/admin/transfer/imports/{id}/cancelcancelTransferImportCancel a site import
POST/_emdash/api/admin/transfer/imports/{id}/abandonabandonTransferImportAbandon a failed or cancelled import
POST/_emdash/api/admin/transfer/imports/{id}/executeexecuteTransferImportStart a planned import
POST/_emdash/api/admin/transfer/imports/{id}/advanceadvanceTransferImportAdvance an executing import
GET/_emdash/api/admin/transfer/imports/{id}/receiptgetTransferImportReceiptGet an import receipt
GET/_emdash/api/admin/transfer/exportslistTransferExportsList site exports
POST/_emdash/api/admin/transfer/exportscreateTransferExportStart a site export
GET/_emdash/api/admin/transfer/exports/{id}getTransferExportGet a site export
POST/_emdash/api/admin/transfer/exports/{id}/advanceadvanceTransferExportAdvance a site export
GET/_emdash/api/admin/transfer/exports/{id}/manifestgetTransferExportManifestDownload an export manifest
GET/_emdash/api/admin/transfer/exports/{id}/files/{path}downloadTransferExportFileDownload one export file
GET/_emdash/api/admin/transfer/exports/{id}/archivedownloadTransferExportArchiveDownload an export archive
GET/_emdash/api/admin/transfer/approvalslistTransferApprovalsList transfer approvals
POST/_emdash/api/admin/transfer/approvals/{id}/approveapproveTransferApprovalApprove a transfer request
POST/_emdash/api/admin/transfer/approvals/{id}/denydenyTransferApprovalDeny 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,确认成功前不会出现在标准库中。

  1. 请求上传目标

    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;使用该媒体项,勿再上传或确认另一份副本。

  2. 上传字节

    使用返回的 method 与 headers。将根相对 URL 解析为 EmDash 站点并包含 Bearer 令牌。向其他源的绝对 URL 仅发送返回的上传 headers。

  3. 确认上传

    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 项在确认成功前不会出现在标准媒体列表中。

上传错误

以下错误需要不同的恢复操作:

状态代码操作
400NO_FILE在 multipart 请求中添加 file 字段,或发送缺失的上传体。
400INVALID_TYPE使用与 pending 媒体项匹配的允许 MIME 类型。
400VALIDATION_ERROR修正缺失或无效的元数据,包括超过配置大小限制的值。
400FILE_NOT_FOUND确认前先将对象上传到返回的目标。
400UPLOAD_SIZE_MISMATCH以正确 size 重新开始流程;声明、上传与确认的 size 必须一致。
400 或 409INVALID_STATE重试前读取媒体项。它可能已非 pending,或在确认期间被其他请求更改。
404NOT_FOUND使用现有的 pending 媒体 ID。
413PAYLOAD_TOO_LARGE减小文件大小或增大 maxUploadSize 后再开始上传。

媒体文件夹

文件夹名称会 trim、限制 200 字符,并在 Unicode 规范化与小写后比较。因此 Photos、photos 与 PHOTOS 等名称会冲突。删除文件夹会将其媒体移回主库;不会删除媒体、更改媒体 ID 或 URL,或更改使用记录。

修复媒体使用

媒体使用激活、进度、工作队列、删除清理与修复是 /_emdash/api/admin/media-usage/ 下的运维操作。会话用户需要 schema:manage;Bearer 令牌还需要 admin scope。

跟踪关闭时,激活前暂停直接写数据库的进程。EmDash 在设置运行期间会暂时阻止通过其 API 发送的内容与 schema 写入,但无法阻止直接写数据库的其他进程。

  1. 停止直接写数据库的进程,等待进行中的写入完成。
  2. 读取激活状态。expanded 表示跟踪关闭,activating 表示 EmDash 正在准备集合,active 表示新的媒体引用变更会被跟踪。
  3. 发送一次激活请求 { "writersDrained": true }。
  4. 逐次发送进度请求,等待每次返回的 nextRequestInMs,直到激活为 active。
  5. 恢复直接写数据库。
  6. 继续进度请求,直到历史索引为 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 清空整个日志。两者均不删除重定向规则。