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정렬 방향: 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

매개변수

매개변수타입설명
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"
		}
	}
}

승인된 상세 정보에는 활성 및 삭제된 항목이 포함됩니다. null이 아닌 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가 필요하고, 라우트가 /_emdash/api/admin 아래에 있기 때문에 Bearer 토큰은 admin 스코프가 필요합니다.

전체 콘텐츠 복구는 현재 버전에서 동기적이고 순차적으로 실행됩니다. 대규모 사이트에서는 비용이 많이 들 수 있으므로, 호출자는 의도적으로 트리거하고 응답을 기다려야 합니다.

요청 본문

하나의 컬렉션 복구:

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

모든 콘텐츠 컬렉션 복구:

{
	"scope": "all"
}

요청 본문은 필수입니다. 유효하지 않은 슬러그, 알 수 없는 요청 키, 누락된 scope, 본문 없는 요청은 전체 콘텐츠 복구를 기본값으로 하는 대신 400을 반환합니다.

응답

복구 호출이 구조화된 결과를 생성하면 엔드포인트는 200을 반환합니다. data.status를 확인하세요: failedstale은 복구 도메인 상태이며 전송 오류가 아닙니다.

{
	"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"COLLECTION_NOT_FOUND와 같은 컬렉션별 lastErrorCode가 포함됩니다. 전송 오류는 400, 401, 403, 413, 500을 포함한 표준 오류 봉투를 계속 사용합니다.

미디어 파일 조회

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_CONFLICT409슬러그가 이미 존재함
RESERVED_SLUG400슬러그가 예약됨

검색 엔드포인트

글로벌 검색

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를 지원합니다. 배포에서 허용된 오리진을 구성하세요.