API 參考
Sites
Management API: GET /sites, POST /sites, GET /sites/{id}, …
List the websites the token owner is a member of, restricted to the websites the token was granted for, most recently updated first. Returns ids you need for every other site-scoped call. Errors: FORBIDDEN, INVALID_CURSOR.
Parameters
limitinteger · queryMaximum number of items to return, 1–100. Defaults to 20.
cursorstring · queryOpaque cursor from the previous page's nextCursor. Omit for the first page.
Response 200
dataobject[]requireddata[].idstringrequireddata[].namestringrequireddata[].rolestring | nullrequiredThe token owner's role on this website (OWNER/ADMIN/MEMBER/VIEWER).
data[].planstringrequireddata[].localestring | nullrequiredContent language of the site (e.g. zh-Hant, en-US).
data[].statusstringrequireddata[].isPublishedbooleanrequireddata[].subdomainstring | nullrequiredAssigned on first publish; null until then.
data[].suggestedSubdomainstring | nullrequiredSuggested slug to use when publishing for the first time.
data[].publicUrlstring | nullrequiredhttps URL on kanorio.com when published.
data[].primaryCustomDomainobject | nullrequireddata[].primaryCustomDomain.hostnamestringrequireddata[].primaryCustomDomain.statusstringrequireddata[].lastPublishedAtstring | nullrequireddata[].hasUnpublishedChangesbooleanrequiredTrue when pages or site-wide layout/theme have unpublished draft edits (publish with site.publish). Blog posts publish individually via posts.publish.
data[].postsWithDraftChangesintegerPublished blog posts that have unpublished draft edits (call posts.publish on each to publish them).
data[].draftPostsintegerBlog posts currently in DRAFT status.
data[].createdAtstringrequireddata[].updatedAtstringrequiredhasMorebooleanrequiredTrue when another page exists; pass nextCursor to fetch it.
nextCursorstring | nullrequiredCursor for the next page, or null on the last page.
Errors
FORBIDDEN403INVALID_CURSOR400
MCP tool sites_list
curl -X GET "https://app.kanorio.com/api/v1/sites" \ -H "Authorization: Bearer $KANORIO_TOKEN"
{
"data": [
{
"id": "ws_example123",
"name": "Sunny Bakery",
"role": "OWNER",
"plan": "PRO",
"locale": "zh-Hant",
"status": "ACTIVE",
"isPublished": true,
"subdomain": "sunny-bakery",
"suggestedSubdomain": "sunny-bakery",
"publicUrl": "https://sunny-bakery.kanorio.com",
"primaryCustomDomain": {
"hostname": "www.sunnybakery.example",
"status": "ACTIVE"
},
"lastPublishedAt": "2026-10-01T02:30:00.000Z",
"hasUnpublishedChanges": true,
"createdAt": "2026-08-12T03:20:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Create a new website owned by the token owner. Requires a token granted for all websites (not restricted to specific websiteIds). mode=blank: creates the skeleton immediately (empty home page, hidden legal pages, a default header and footer in globalSections, owner membership) and returns websiteId. Then follow sections.describe -> conventions.brandFirst: set the theme (site.update_settings), define buttons (globals.update_site_settings), create pages, fill them with pages.replace_sections, rebuild the header navigation (globals.replace_header) and publish. mode=brief: queues the AI site builder with a brand brief and returns operationId; poll operations.get every 5-10 seconds until status is SUCCEEDED (result.websiteId is set) or FAILED. This consumes the owner's AI credits and daily site-creation quota. Errors: TOKEN_WEBSITE_SCOPED, VALIDATION, DAILY_LIMIT_REACHED (brief).
Response 201
mode"blank" | "brief"requiredwebsiteIdstring | nullrequiredSet immediately for blank; for brief it appears on operations.get once the job succeeds.
operationIdstring | nullrequiredPoll with operations.get (brief mode only).
jobIdstring | nullrequiredDeprecated alias of the underlying job id; use operationId with operations.get.
statusstring | nullrequiredJob status for brief mode.
Errors
TOKEN_WEBSITE_SCOPED403VALIDATION400DAILY_LIMIT_REACHED429 · brief
MCP tool sites_create
curl -X POST "https://app.kanorio.com/api/v1/sites" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'{
"mode": "blank",
"websiteId": "ws_example123",
"operationId": null,
"jobId": null,
"status": null
}Get one website's summary: plan, publish state, subdomain, primary custom domain and whether there are unpublished changes. Errors: FORBIDDEN (also when the website does not exist), WEBSITE_NOT_FOUND.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
idstringrequirednamestringrequiredrolestring | nullrequiredThe token owner's role on this website (OWNER/ADMIN/MEMBER/VIEWER).
planstringrequiredlocalestring | nullrequiredContent language of the site (e.g. zh-Hant, en-US).
statusstringrequiredisPublishedbooleanrequiredsubdomainstring | nullrequiredAssigned on first publish; null until then.
suggestedSubdomainstring | nullrequiredSuggested slug to use when publishing for the first time.
publicUrlstring | nullrequiredhttps URL on kanorio.com when published.
primaryCustomDomainobject | nullrequiredprimaryCustomDomain.hostnamestringrequiredprimaryCustomDomain.statusstringrequiredlastPublishedAtstring | nullrequiredhasUnpublishedChangesbooleanrequiredTrue when pages or site-wide layout/theme have unpublished draft edits (publish with site.publish). Blog posts publish individually via posts.publish.
postsWithDraftChangesintegerPublished blog posts that have unpublished draft edits (call posts.publish on each to publish them).
draftPostsintegerBlog posts currently in DRAFT status.
createdAtstringrequiredupdatedAtstringrequired
Errors
FORBIDDEN403 · also when the website does not existWEBSITE_NOT_FOUND404
MCP tool sites_get
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"id": "ws_example123",
"name": "Sunny Bakery",
"role": "OWNER",
"plan": "PRO",
"locale": "zh-Hant",
"status": "ACTIVE",
"isPublished": true,
"subdomain": "sunny-bakery",
"suggestedSubdomain": "sunny-bakery",
"publicUrl": "https://sunny-bakery.kanorio.com",
"primaryCustomDomain": {
"hostname": "www.sunnybakery.example",
"status": "ACTIVE"
},
"lastPublishedAt": "2026-10-01T02:30:00.000Z",
"hasUnpublishedChanges": true,
"createdAt": "2026-08-12T03:20:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z"
}Return the visual sitemap tree of a website: top-level pages with nested subpages (up to 3 levels), real page status (published / changed / draft / hidden), the ordered list of section types on each page, and summary counts for Kanorio-managed Store and Blog pages. Errors: FORBIDDEN (also when the website does not exist), WEBSITE_NOT_FOUND.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
websiteIdstringrequiredtotalGeneralPagesintegerrequiredmaxDepthintegerrequiredrootPagesobject[]requiredrootPages[].idstringrequiredrootPages[].slugstringrequiredrootPages[].titlestringrequiredrootPages[].pageTypestringrequiredrootPages[].isHomePagebooleanrequiredrootPages[].isVisiblebooleanrequiredrootPages[].isDirectorybooleanrequiredrootPages[].status"published" | "changed" | "draft" | "hidden"requiredrootPages[].pathstring[]requiredrootPages[].parentSlugstring | nullrequiredrootPages[].depthintegerrequiredrootPages[].sectionsstring[]requiredOrdered list of content section types on this page (excluding global header and footer).
rootPages[].hasGlobalChromebooleanrequiredrootPages[].descendantCountintegerrequiredrootPages[].childrenany[]requiredsystemGroupsobjectrequiredsystemGroups.shopobjectrequiredsystemGroups.shop.basePathstringrequiredsystemGroups.shop.productCountintegerrequiredActive non-template products in the store (excludes sample products).
systemGroups.shop.productCategoryCountintegerrequiredsystemGroups.blogobjectrequiredsystemGroups.blog.basePathstringrequiredsystemGroups.blog.publishedPostCountintegerrequiredsystemGroups.blog.blogCategoryCountintegerrequiredsystemGroups.blog.blogTagCountintegerrequired
Errors
FORBIDDEN403 · also when the website does not existWEBSITE_NOT_FOUND404
MCP tool sites_structure
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/structure" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"websiteId": "ws_example123",
"totalGeneralPages": 2,
"maxDepth": 1,
"rootPages": [
{
"id": "pg_example456",
"slug": "/",
"title": "Home",
"pageType": "generic",
"isHomePage": true,
"isVisible": true,
"isDirectory": false,
"status": "published",
"path": [],
"parentSlug": null,
"depth": 0,
"sections": [
"page-main-visual",
"faq"
],
"hasGlobalChrome": true,
"descendantCount": 0,
"children": []
},
{
"id": "pg_example789",
"slug": "/about",
"title": "About Us",
"pageType": "generic",
"isHomePage": false,
"isVisible": true,
"isDirectory": false,
"status": "published",
"path": [
"about"
],
"parentSlug": null,
"depth": 1,
"sections": [
"image-text-split"
],
"hasGlobalChrome": true,
"descendantCount": 0,
"children": []
}
],
"systemGroups": {
"shop": {
"basePath": "/shop",
"productCount": 12,
"productCategoryCount": 3
},
"blog": {
"basePath": "/blog",
"publishedPostCount": 27,
"blogCategoryCount": 5,
"blogTagCount": 14
}
}
}Get the website's plan tier, storage quota & usage (usedBytes, limitBytes, remainingBytes), and content counts (general pages, blog posts, active products, custom domains) so you can check limits before uploading or creating resources. Errors: FORBIDDEN.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
websiteIdstringrequiredWebsite id.
planstringrequiredCurrent plan tier (FREE, PRO, BUSINESS).
storageobjectrequiredstorage.usedBytesnumberrequiredBytes counted against the storage limit.
storage.limitBytesnumberrequiredPlan storage limit in bytes (including add-on levels).
storage.remainingBytesnumberrequiredRemaining storage in bytes.
storage.scope"website" | "account"requiredWhether the storage pool is per-website or shared across the owner's FREE sites.
countsobjectrequiredcounts.generalPagesintegerrequiredTotal non-product pages.
counts.visiblePagesintegerrequiredVisible non-product pages.
counts.publishedPostsintegerrequiredPublished blog posts.
counts.draftPostsintegerrequiredDraft blog posts.
counts.activeProductsintegerrequiredActive non-template products.
counts.customDomainsintegerrequiredBound custom domains.
Errors
FORBIDDEN403
MCP tool sites_usage
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/usage" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"websiteId": "ws_example123",
"plan": "PRO",
"storage": {
"usedBytes": 482133,
"limitBytes": 10737418240,
"remainingBytes": 10736936107,
"scope": "website"
},
"counts": {
"generalPages": 5,
"visiblePages": 4,
"publishedPosts": 12,
"draftPosts": 1,
"activeProducts": 8,
"customDomains": 1
}
}Run a one-call health and readiness diagnosis on the website: checks unpublished page/theme changes, unpublished blog post edits, missing media files, pending custom domain DNS, search engine indexing status, empty SEO meta descriptions, and unedited sample products. Each finding includes severity and fixWith (the capability or dashboard path to resolve it). Errors: FORBIDDEN.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
websiteIdstringrequiredWebsite id.
overallStatus"healthy" | "attention" | "action_required"requiredSummary health status.
findingsobject[]requiredfindings[].idstringrequiredFinding identifier.
findings[].severity"info" | "warn" | "error"requiredSeverity level.
findings[].summarystringrequiredHuman-readable explanation of the finding.
findings[].fixWithstringrequiredCapability name or dashboard route to resolve this finding.
Errors
FORBIDDEN403
MCP tool sites_diagnose
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/diagnose" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"websiteId": "ws_example123",
"overallStatus": "attention",
"findings": [
{
"id": "unpublished_page_changes",
"severity": "info",
"summary": "There are unpublished draft changes on pages or site-wide layout.",
"fixWith": "site.publish"
}
]
}