Kanorio

API 參考

Sites

Management API: GET /sites, POST /sites, GET /sites/{id}, …

這頁只有英文版。API 參考、Webhook 事件與變更紀錄維持英文,和程式碼裡的欄位名稱、錯誤碼一致。
Base URL https://app.kanorio.com/api/v1ErrorsPagination

List sites

GET/sites
betaconsole:viewdefault (120/minute per token)

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 · query

    Maximum number of items to return, 1–100. Defaults to 20.

  • cursorstring · query

    Opaque cursor from the previous page's nextCursor. Omit for the first page.

Response 200

  • dataobject[]required
  • data[].idstringrequired
  • data[].namestringrequired
  • data[].rolestring | nullrequired

    The token owner's role on this website (OWNER/ADMIN/MEMBER/VIEWER).

  • data[].planstringrequired
  • data[].localestring | nullrequired

    Content language of the site (e.g. zh-Hant, en-US).

  • data[].statusstringrequired
  • data[].isPublishedbooleanrequired
  • data[].subdomainstring | nullrequired

    Assigned on first publish; null until then.

  • data[].suggestedSubdomainstring | nullrequired

    Suggested slug to use when publishing for the first time.

  • data[].publicUrlstring | nullrequired

    https URL on kanorio.com when published.

  • data[].primaryCustomDomainobject | nullrequired
  • data[].primaryCustomDomain.hostnamestringrequired
  • data[].primaryCustomDomain.statusstringrequired
  • data[].lastPublishedAtstring | nullrequired
  • data[].hasUnpublishedChangesbooleanrequired

    True when pages or site-wide layout/theme have unpublished draft edits (publish with site.publish). Blog posts publish individually via posts.publish.

  • data[].postsWithDraftChangesinteger

    Published blog posts that have unpublished draft edits (call posts.publish on each to publish them).

  • data[].draftPostsinteger

    Blog posts currently in DRAFT status.

  • data[].createdAtstringrequired
  • data[].updatedAtstringrequired
  • hasMorebooleanrequired

    True when another page exists; pass nextCursor to fetch it.

  • nextCursorstring | nullrequired

    Cursor for the next page, or null on the last page.

Errors

  • FORBIDDEN 403
  • INVALID_CURSOR 400

MCP tool sites_list

curl -X GET "https://app.kanorio.com/api/v1/sites" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "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 site

POST/sites
betaconsole:viewdefault (120/minute per token)

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"required
  • websiteIdstring | nullrequired

    Set immediately for blank; for brief it appears on operations.get once the job succeeds.

  • operationIdstring | nullrequired

    Poll with operations.get (brief mode only).

  • jobIdstring | nullrequired

    Deprecated alias of the underlying job id; use operationId with operations.get.

  • statusstring | nullrequired

    Job status for brief mode.

Errors

  • TOKEN_WEBSITE_SCOPED 403
  • VALIDATION 400
  • DAILY_LIMIT_REACHED 429 · 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 '{}'
Response
{
  "mode": "blank",
  "websiteId": "ws_example123",
  "operationId": null,
  "jobId": null,
  "status": null
}

Retrieve a site

GET/sites/{id}
betaconsole:viewdefault (120/minute per token)

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 · pathrequired

    Website id. Get it from sites.list or from sites.create.

Response 200

  • idstringrequired
  • namestringrequired
  • rolestring | nullrequired

    The token owner's role on this website (OWNER/ADMIN/MEMBER/VIEWER).

  • planstringrequired
  • localestring | nullrequired

    Content language of the site (e.g. zh-Hant, en-US).

  • statusstringrequired
  • isPublishedbooleanrequired
  • subdomainstring | nullrequired

    Assigned on first publish; null until then.

  • suggestedSubdomainstring | nullrequired

    Suggested slug to use when publishing for the first time.

  • publicUrlstring | nullrequired

    https URL on kanorio.com when published.

  • primaryCustomDomainobject | nullrequired
  • primaryCustomDomain.hostnamestringrequired
  • primaryCustomDomain.statusstringrequired
  • lastPublishedAtstring | nullrequired
  • hasUnpublishedChangesbooleanrequired

    True when pages or site-wide layout/theme have unpublished draft edits (publish with site.publish). Blog posts publish individually via posts.publish.

  • postsWithDraftChangesinteger

    Published blog posts that have unpublished draft edits (call posts.publish on each to publish them).

  • draftPostsinteger

    Blog posts currently in DRAFT status.

  • createdAtstringrequired
  • updatedAtstringrequired

Errors

  • FORBIDDEN 403 · also when the website does not exist
  • WEBSITE_NOT_FOUND 404

MCP tool sites_get

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "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"
}

Site structure

GET/sites/{id}/structure
betaeditor:enterdefault (120/minute per token)

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 · pathrequired

    Website id. Get it from sites.list or from sites.create.

Response 200

  • websiteIdstringrequired
  • totalGeneralPagesintegerrequired
  • maxDepthintegerrequired
  • rootPagesobject[]required
  • rootPages[].idstringrequired
  • rootPages[].slugstringrequired
  • rootPages[].titlestringrequired
  • rootPages[].pageTypestringrequired
  • rootPages[].isHomePagebooleanrequired
  • rootPages[].isVisiblebooleanrequired
  • rootPages[].isDirectorybooleanrequired
  • rootPages[].status"published" | "changed" | "draft" | "hidden"required
  • rootPages[].pathstring[]required
  • rootPages[].parentSlugstring | nullrequired
  • rootPages[].depthintegerrequired
  • rootPages[].sectionsstring[]required

    Ordered list of content section types on this page (excluding global header and footer).

  • rootPages[].hasGlobalChromebooleanrequired
  • rootPages[].descendantCountintegerrequired
  • rootPages[].childrenany[]required
  • systemGroupsobjectrequired
  • systemGroups.shopobjectrequired
  • systemGroups.shop.basePathstringrequired
  • systemGroups.shop.productCountintegerrequired

    Active non-template products in the store (excludes sample products).

  • systemGroups.shop.productCategoryCountintegerrequired
  • systemGroups.blogobjectrequired
  • systemGroups.blog.basePathstringrequired
  • systemGroups.blog.publishedPostCountintegerrequired
  • systemGroups.blog.blogCategoryCountintegerrequired
  • systemGroups.blog.blogTagCountintegerrequired

Errors

  • FORBIDDEN 403 · also when the website does not exist
  • WEBSITE_NOT_FOUND 404

MCP tool sites_structure

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/structure" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "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
    }
  }
}

Site usage

GET/sites/{id}/usage
betaconsole:viewdefault (120/minute per token)

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 · pathrequired

    Website id. Get it from sites.list or from sites.create.

Response 200

  • websiteIdstringrequired

    Website id.

  • planstringrequired

    Current plan tier (FREE, PRO, BUSINESS).

  • storageobjectrequired
  • storage.usedBytesnumberrequired

    Bytes counted against the storage limit.

  • storage.limitBytesnumberrequired

    Plan storage limit in bytes (including add-on levels).

  • storage.remainingBytesnumberrequired

    Remaining storage in bytes.

  • storage.scope"website" | "account"required

    Whether the storage pool is per-website or shared across the owner's FREE sites.

  • countsobjectrequired
  • counts.generalPagesintegerrequired

    Total non-product pages.

  • counts.visiblePagesintegerrequired

    Visible non-product pages.

  • counts.publishedPostsintegerrequired

    Published blog posts.

  • counts.draftPostsintegerrequired

    Draft blog posts.

  • counts.activeProductsintegerrequired

    Active non-template products.

  • counts.customDomainsintegerrequired

    Bound custom domains.

Errors

  • FORBIDDEN 403

MCP tool sites_usage

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/usage" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "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
  }
}

Diagnose a site

GET/sites/{id}/diagnose
betaconsole:viewdefault (120/minute per token)

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 · pathrequired

    Website id. Get it from sites.list or from sites.create.

Response 200

  • websiteIdstringrequired

    Website id.

  • overallStatus"healthy" | "attention" | "action_required"required

    Summary health status.

  • findingsobject[]required
  • findings[].idstringrequired

    Finding identifier.

  • findings[].severity"info" | "warn" | "error"required

    Severity level.

  • findings[].summarystringrequired

    Human-readable explanation of the finding.

  • findings[].fixWithstringrequired

    Capability name or dashboard route to resolve this finding.

Errors

  • FORBIDDEN 403

MCP tool sites_diagnose

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/diagnose" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "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"
    }
  ]
}