HTTP API 레퍼런스
베이스 URL: https://api.roottale.com
인증: 모든 요청에 Authorization: Bearer rtlk_cust_... 헤더.
JS/TS 스택은 raw 호출 대신 @roottale/cms-client를 사용하세요 — 에러 처리,
브라우저 노출 방지, 전화번호 포맷 등이 포함되어 있습니다.
GET /v1/cms/public/posts
발행된 글 목록 (커서 페이지네이션).
| 쿼리 | 설명 |
|---|---|
limit | 페이지 크기 (서버에서 상한 clamp) |
cursor | 이전 응답의 next_cursor |
type | post | page |
site_id | 멀티 사이트 키일 때만 — site-scoped 키면 생략 |
{
"items": [ { "id": "…", "slug": "…", "title": "…", "body_json": {…},
"terms": [{ "taxonomy": "category", "name": "…", "slug": "…" }],
"published_at": "…" } ],
"has_more": false,
"next_cursor": null
}
GET /v1/cms/public/posts/{identifier}
글 1개 — identifier는 slug 또는 UUID. 미발행/없는 글은 404.
GET /v1/cms/public/search
발행된 글 키워드 검색 (사이트 내 검색, WP ?s= 패리티). title·excerpt·본문
텍스트의 case-insensitive 부분일치, 최신 발행순. 응답은 카드 렌더용 슬림
hit — 본문(body_json)은 미포함이므로 상세는 slug 로 글 1개 API를 호출하세요.
| 쿼리 | 설명 |
|---|---|
q | 검색 키워드 (필수, 1~100자) |
limit | 결과 수 1~50, 기본 10 |
type | post(기본) | page |
site_id | 멀티 사이트 키일 때만 |
{ "tenant_id": "…", "site_id": "…", "query": "세무",
"items": [ { "id": "…", "type": "post", "title": "…", "slug": "…",
"excerpt": "…", "featured_media_url": "…",
"published_at": "…" } ] }
JS/TS 는 @roottale/cms-client/server 의 searchPosts({ apiKey, query }) 를
사용하세요 — 구 서버(라우트 미배포)의 404 를 빈 배열로 처리합니다.
GET /v1/cms/public/menus
네비게이션 메뉴 전체 — 어드민 "디자인 > 메뉴" 저장값. 항목은 깊이 2 트리.
{ "tenant_id": "…", "site_id": "…",
"items": [ { "id": "…", "name": "헤더 메뉴", "slug": "primary",
"items": [ { "id": "…", "label": "회사 소개", "url": "/about",
"children": [ { "id": "…", "label": "오시는 길",
"url": "/about/location" } ] },
{ "id": "…", "label": "블로그", "url": "/blog" } ],
"updated_at": "…" } ] }
GET /v1/cms/public/menus/{slug}
위치 핸들(primary, footer 등)로 메뉴 1개. 없으면 404 — 사이트는 자체
fallback 네비를 렌더하세요 (menus.md 참고).
GET /v1/cms/public/theme
어드민에서 설정한 디자인 토큰. 설정된 그룹만 포함됩니다.
{ "tenant_id": "…", "site_id": "…",
"colors": {…}, "fonts": {…}, "radius": {…}, "updated_at": "…" }
GET /v1/cms/public/blog-settings
블로그 표시 설정 (TOC·작성자·발행일·작성자 카드, 저자 프로필).
{ "show_table_of_contents": false, "show_author": true, "show_date": true,
"show_author_card": true, "toc_title": null,
"author_profile_name": null, "author_profile_bio": null,
"author_profile_image_url": null, "author_profile_image_radius": "circle",
"updated_at": null }
GET /v1/cms/public/business-profile
비즈니스 프로필 (로컬 SEO) — 어드민 "운영 > 비즈니스 프로필" 저장값.
미설정이면 configured: false + 필드 null.
{ "tenant_id": "…", "site_id": "…", "configured": true,
"name": "길동세무회계", "legal_name": null,
"business_type": "AccountingService",
"telephone": "02-1234-5678", "email": null,
"address": { "street_address": "테헤란로 123", "address_locality": "강남구",
"address_region": "서울특별시", "postal_code": "06234" },
"geo": { "latitude": 37.5006, "longitude": 127.0364 },
"opening_hours": [ { "days": ["Mo","Tu","We","Th","Fr"],
"opens": "09:00", "closes": "18:00" } ],
"price_range": "₩₩", "area_served": ["서울 강남구"],
"profiles": { "naver_place": "https://…", "google_business": "https://…",
"kakao_channel": null, "instagram": null, "naver_blog": null },
"updated_at": "…" }
business_type: LocalBusiness | ProfessionalService |
AccountingService | LegalService | MedicalClinic | Dentist |
RealEstateAgent | Restaurant | BeautySalon.
GET /v1/cms/public/analytics
분석 태그 설정.
{ "tags": [ { "provider": "ga4", "id": "G-XXXXXXX", "enabled": true } ] }
provider: ga4 | clarity | meta_pixel | naver.
GET /v1/cms/public/jwks
웹훅 서명 검증용 site-scoped JWKS 공개키. site-scoped 키 필수 — 테넌트
전체 키로 호출하면 400 site_scope_required.
POST /v1/public/inquiries
상담문의(리드) 접수. multipart/form-data.
| 필드 | 필수 | 비고 |
|---|---|---|
vertical | ✅ | consulting | medical | tax | legal |
contact_name | ✅ | |
business_name | ✅ | |
email | ✅ | .+@.+\..+ |
phone | ✅ | |
privacy_consent | ✅ | 체크박스 (on) |
overseas_transfer_consent | medical 시 ✅ | |
message, consultation_field, current_site_url | ||
lead_kind | patient(기본) | sales | |
_redirect_url | 완료 후 redirect base (allowlist 검증) | |
attr_landing_path, attr_rt_src, attr_utm_source, attr_utm_medium, attr_utm_campaign, attr_referrer, attr_first_touch_at | 유입 어트리뷰션 — CRM에 유입 경로 표시 (inquiries.md 참고). 그 외 attr_* 키는 무시 | |
| 기타 임의 필드 | 최대 50개, 암호화 보관, CRM 상세 노출 |
응답: 302 redirect — 성공 ?ok=1, 실패 ?err=<code>
(consent_privacy, consent_overseas, invalid_vertical, missing_fields,
invalid_email, internal). 잘못된 키는 401.
서버-서버 호출 시 redirect를 따라가지 말고(redirect: "manual") Location
헤더를 파싱하세요 — @roottale/cms-client의 submitInquiry가 이를 대신합니다.
POST /v1/cms/revalidate
수동 캐시 갱신 트리거 (등록된 웹훅으로 재발송).
{ "event": "post.updated", "paths": ["/blog", "/blog/my-post"], "slug": "my-post" }
에러 형식
비 2xx 응답은 JSON 에러 바디(code, message)를 가집니다. 주요 코드:
invalid_key(401), insufficient_scope(403), rate_limited(429),
not_found(404).
캐시 헤더
공개 콘텐츠 응답은 private 캐시 헤더로 내려갑니다 — CDN 공유 캐시에 의존하지 말고 사이트 측 ISR + 발행 웹훅 조합을 사용하세요.