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
參數
| 參數 | 型別 | 描述 |
|---|---|---|
collection | string | 集合識別碼(路徑) |
cursor | string | 分頁游標(查詢) |
limit | number | 每頁項目數(查詢,預設:50) |
status | string | 依狀態篩選(查詢) |
orderBy | string | 排序欄位(查詢) |
order | string | 排序方向:asc 或 desc(查詢) |
回應
{
"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
參數
| 參數 | 型別 | 描述 |
|---|---|---|
cursor | string | 不透明分頁游標 |
limit | number | 每頁項目數,1 到 100(預設:50) |
mimeType | string | 依一個或多個以逗號分隔的 MIME 型別篩選 |
q | string | 不區分大小寫的檔案名稱搜尋 |
includeUsage | 1 | 在每個回傳項目上包含覆蓋率感知的 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:read 和 content: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 標識資源回收桶項目。來源為 columns 或 draft_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.status:failed 和 stale 是修復領域狀態,不是傳輸錯誤。
{
"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"
}
]
}
}
頂層回應欄位:
| 欄位 | 型別 | 描述 |
|---|---|---|
status | complete | partial | failed | stale | 彙總修復狀態 |
indexedSourceCount | number | 修復期間索引的來源 |
failedSourceCount | number | 修復期間失敗的來源 |
skippedSourceCount | number | 跳過的來源,包括過時衝突 |
deletedSourceCount | number | 修復期間刪除的過時使用列 |
collections | array | 每個集合的修復摘要 |
集合摘要欄位:
| 欄位 | 型別 | 描述 |
|---|---|---|
collection | string | 集合識別碼 |
status | complete | partial | failed | stale | 集合修復狀態 |
indexedSourceCount | number | 此集合索引的來源 |
failedSourceCount | number | 此集合失敗的來源 |
skippedSourceCount | number | 此集合跳過的來源 |
deletedSourceCount | number | 此集合刪除的過時使用列 |
lastErrorCode | string | null | 最後的集合修復錯誤(如果可用) |
startedAt | string | 修復開始時間 |
completedAt | string | null | 完成時間,過時結果為 null |
未知集合回傳 200,帶有 data.status: "failed" 和每個集合的 lastErrorCode(如 COLLECTION_NOT_FOUND)。傳輸錯誤仍使用標準錯誤信封,包括 400、401、403、413 和 500。
取得媒體檔案
GET /_emdash/api/media/file/:key
提供實際的檔案內容。僅用於本地儲存。
修訂版端點
列出修訂版
GET /_emdash/api/content/:collection/:entryId/revisions
參數
| 參數 | 型別 | 描述 |
|---|---|---|
limit | number | 回傳的最大修訂版數(預設: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
參數
| 參數 | 型別 | 描述 |
|---|---|---|
includeFields | boolean | 包含欄位定義(查詢) |
建立集合
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
參數
| 參數 | 型別 | 描述 |
|---|---|---|
force | boolean | 即使集合有內容也刪除(查詢) |
列出欄位
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_FOUND | 404 | 資源未找到 |
VALIDATION_ERROR | 400 | 無效的輸入資料 |
UNAUTHORIZED | 401 | 權杖缺失或無效 |
FORBIDDEN | 403 | 權限不足 |
CONTENT_LIST_ERROR | 500 | 列出內容失敗 |
CONTENT_CREATE_ERROR | 500 | 建立內容失敗 |
CONTENT_UPDATE_ERROR | 500 | 更新內容失敗 |
CONTENT_DELETE_ERROR | 500 | 刪除內容失敗 |
MEDIA_LIST_ERROR | 500 | 列出媒體失敗 |
MEDIA_CREATE_ERROR | 500 | 建立媒體失敗 |
SCHEMA_CREATE_ERROR | 500 | 架構操作失敗 |
SLUG_CONFLICT | 409 | Slug 已存在 |
RESERVED_SLUG | 400 | Slug 被保留 |
搜尋端點
全域搜尋
GET /_emdash/api/search?q=hello+world
參數
| 參數 | 型別 | 描述 |
|---|---|---|
q | string | 搜尋查詢(必填) |
collections | string | 逗號分隔的集合識別碼 |
status | string | 依狀態篩選(預設:published) |
limit | number | 最大結果數(預設:20) |
cursor | string | 分頁游標 |
回應
{
"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。在部署中設定允許的來源。