API 参考
Site
Management API: GET /sites/{id}/search, GET /sites/{id}/settings, PATCH /sites/{id}/settings, …
Search the live site the way visitors do: published pages, blog posts, blog categories and tags, products and product categories. Use it to check whether the site already covers a topic before writing, or to find the page to edit. Every term in q must appear in the title or body (AND); full-width characters, case and term order do not matter. Drafts, hidden pages and pages excluded from site search are not included; an unpublished site returns no results. Results are ranked; pass limit and cursor (nextCursor) for more. Multilingual sites: pass locale (code or URL prefix) to search one published locale's translated text and URLs; omitted or unknown locales search the primary locale, and resolvedLocale says which was searched. Errors: FORBIDDEN, VALIDATION (details.reason: QUERY_REQUIRED, QUERY_TOO_SHORT or QUERY_TOO_LONG), SEARCH_BUSY, INVALID_CURSOR.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
qstring · queryrequiredSearch terms separated by spaces (up to 4 are used). One term needs 2 CJK or Thai characters, or 3 letters or digits.
kind"PAGE" | "COLLECTION_ITEM" | "PRODUCT" | "CATEGORY"[] · queryOnly these kinds. REST accepts kind=PAGE,PRODUCT. Omit for all kinds.
localestring · queryLocale to search: one of the site's published locales as a code (en-US) or URL prefix (en). Omit for the primary locale; anything else also falls back to the primary locale.
limitinteger · queryMaximum number of items to return, 1–20. Defaults to 20.
cursorstring · queryOpaque cursor from the previous page's nextCursor. Omit for the first page.
Response 200
dataobject[]requireddata[].kind"PAGE" | "COLLECTION_ITEM" | "PRODUCT" | "CATEGORY"requireddata[].collectionKeystring | nullrequiredSet for COLLECTION_ITEM: blog (posts), blog_categories or blog_tags.
data[].refIdstringrequiredId of the source: page id, collection item id, product id or product category id.
data[].slugstring | nullrequiredSource slug; null for pages (use url).
data[].titlestringrequireddata[].urlstring | nullrequiredPath on the live site, e.g. /about or /blog/hello (with the locale's URL prefix when a secondary locale was searched, e.g. /en/about). null for custom-collection items whose collection has no search.pathTemplate: route by collectionKey + slug or fetch the item from the Delivery API.
data[].excerptstringrequireddata[].snippetstringrequiredBody text around the first matching term.
data[].imageUrlstring | nullrequireddata[].soldOutbooleanrequiredProducts with nothing left to buy; always sorted last.
data[].scorenumberrequiredRelevance used for ordering; compare only within one response.
data[].productobject | nulldata[].product.amountinteger | nullrequireddata[].product.currencystringrequireddata[].product.showPlusbooleanrequireddata[].product.compareAtAmountinteger | nullrequireddata[].product.isPromobooleandata[].product.promoBadgestring | nulldata[].product.ratingnumber | nullrequireddata[].product.reviewCountinteger | nullrequireddata[].product.totalSalesCountinteger | nullrequireddata[].blogobject | nulldata[].blog.categoryNamestring | nullrequireddata[].blog.authorsstring[]requireddata[].blog.publishedAtstring | nullrequireddata[].blog.readingTimeMininteger | nullrequiredhasMorebooleanrequiredTrue when another page exists; pass nextCursor to fetch it.
nextCursorstring | nullrequiredCursor for the next page, or null on the last page.
qstringrequiredThe normalized query actually searched.
tokensstring[]requiredTerms that all had to match.
resolvedLocalestringrequiredLocale actually searched (the primary locale when locale was omitted or is not a published locale).
Errors
FORBIDDEN403VALIDATION400 · details.reason: QUERY_REQUIRED, QUERY_TOO_SHORT or QUERY_TOO_LONGSEARCH_BUSY429INVALID_CURSOR400
MCP tool site_search
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/search?q=sourdough&kind=PAGE%2CCOLLECTION_ITEM&limit=5" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"q": "sourdough",
"tokens": [
"sourdough"
],
"data": [
{
"kind": "PAGE",
"collectionKey": null,
"refId": "pg_example789",
"slug": null,
"title": "About Us",
"url": "/about",
"excerpt": "Naturally leavened sourdough baked fresh every morning.",
"snippet": "Every loaf starts with a sourdough starter we have fed daily since 2019.",
"imageUrl": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
"soldOut": false,
"score": 0.412
},
{
"kind": "COLLECTION_ITEM",
"collectionKey": "blog",
"refId": "post_example01",
"slug": "starter-guide",
"title": "How we keep our starter alive",
"url": "/blog/starter-guide",
"excerpt": "A week in the life of our 7-year-old starter.",
"snippet": "…the sourdough starter goes into the fridge on Sunday nights…",
"imageUrl": null,
"soldOut": false,
"score": 0.087
}
],
"resolvedLocale": "en-US",
"hasMore": false,
"nextCursor": null
}Read the website's brand identity (brand colors, fonts, corner radius, color scheme), hosted layout design (density, text sizes), content language and website currency. Errors: FORBIDDEN (also when the website does not exist).
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
brandobjectrequiredBrand identity (takes effect immediately on the live site, checkout, sign-in, customer account, emails, certificates and the Delivery API): displayName, logo?, brandColor { light, dark } (hex), brandForeground? { light?, dark? }, typography { bodyEnFontId, bodyZhHantFontId, bodyZhHansFontId, bodyJaFontId, bodyKoFontId }, cornerRadius (sharp|normal|soft), colorScheme (system|light|dark).
themeobjectrequiredLayout design of the hosted site (draft until site.publish): sectionDensity (standard|compact), textSizeSettings?. Brand fields (brandColor, brandForeground, typography, cornerRadius, colorScheme) are not part of theme; read and write them in brand.
localestringrequiredContent language (BCP 47).
currencystringrequiredWebsite currency (ISO 4217, read-only here): product prices, checkout and every amount on the site use it. The owner changes it in website settings.
Errors
FORBIDDEN403 · also when the website does not exist
MCP tool site_get_settings
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/settings" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"brand": {
"displayName": "Sunny Bakery",
"brandColor": {
"light": "#D9822B",
"dark": "#F0A55A"
},
"typography": {
"bodyEnFontId": "ubuntu",
"bodyZhHantFontId": "noto-sans-tc",
"bodyZhHansFontId": "noto-sans-sc",
"bodyJaFontId": "noto-sans-jp",
"bodyKoFontId": "noto-sans-kr"
},
"cornerRadius": "soft",
"colorScheme": "system"
},
"theme": {
"sectionDensity": "standard"
},
"locale": "zh-Hant",
"currency": "TWD"
}Update the website's brand identity, hosted layout design and basic settings; only the fields you send change. brand (takes effect immediately everywhere the brand appears: site, checkout, sign-in, customer account, emails, certificates, Delivery API): brandColor { light, dark } hex colors for buttons, links and accents; brandForeground overrides the text color on brand-colored surfaces (null resets to automatic contrast); typography picks body font ids per script (bodyEnFontId, bodyZhHantFontId, bodyZhHansFontId, bodyJaFontId, bodyKoFontId); cornerRadius: sharp | normal | soft; colorScheme: system follows the visitor's device, light or dark fixes the site, checkout, sign-in and customer account to one mode. Logo and display name are managed in the website's brand settings page, not here. Layout (hosted site only, draft until site.publish): sectionDensity: standard | compact; textSizeSettings. locale changes the content language (fonts, HTML lang, legal pages). The website currency can't be changed here: it re-labels every price and is limited by the connected payment providers, so the owner changes it in website settings. Set brand before filling pages so the colors apply everywhere. Errors: FORBIDDEN (also when the website does not exist), VALIDATION (details lists the failing field), WRITE_FROZEN.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
settingsobjectrequiredFields to change; omitted fields keep their current value.
settings.sectionDensity"standard" | "compact"settings.textSizeSettingsobject | nullsettings.textSizeSettings.h1integersettings.textSizeSettings.h2integersettings.textSizeSettings.h3integersettings.textSizeSettings.bodyintegersettings.locale"ar" | "bn" | "bg" | "my" | "zh-CN" | "zh-HK" | "zh-Hant" | "cs" | "da" | "nl-BE" | "nl-NL" | "en-AU" | "en-CA" | "en-GB" | "en-US" | "tl" | "fi" | "fr-CA" | "fr-FR" | "de" | "el" | "gu" | "he" | "hi" | "hu" | "id" | "it" | "ja" | "kn" | "km" | "ko" | "lo" | "ms" | "ml" | "mr" | "ne" | "nb" | "nn" | "fa" | "pl" | "pt-BR" | "pt-PT" | "pa" | "ro" | "ru" | "si" | "es-419" | "es-ES" | "sw" | "sv" | "ta" | "te" | "th" | "tr" | "uk" | "ur" | "vi"settings.brandobjectsettings.brand.brandColorobjectsettings.brand.brandForegroundobject | nullsettings.brand.typographyobjectsettings.brand.cornerRadius"sharp" | "normal" | "soft"settings.brand.colorScheme"system" | "light" | "dark"
Response 200
brandobjectrequiredBrand identity (takes effect immediately on the live site, checkout, sign-in, customer account, emails, certificates and the Delivery API): displayName, logo?, brandColor { light, dark } (hex), brandForeground? { light?, dark? }, typography { bodyEnFontId, bodyZhHantFontId, bodyZhHansFontId, bodyJaFontId, bodyKoFontId }, cornerRadius (sharp|normal|soft), colorScheme (system|light|dark).
themeobjectrequiredLayout design of the hosted site (draft until site.publish): sectionDensity (standard|compact), textSizeSettings?. Brand fields (brandColor, brandForeground, typography, cornerRadius, colorScheme) are not part of theme; read and write them in brand.
localestringrequiredContent language (BCP 47).
currencystringrequiredWebsite currency (ISO 4217, read-only here): product prices, checkout and every amount on the site use it. The owner changes it in website settings.
Errors
FORBIDDEN403 · also when the website does not existVALIDATION400 · details lists the failing fieldWRITE_FROZEN503
MCP tool site_update_settings
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/settings" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"settings":{"brand":{"brandColor":{"light":"#D9822B","dark":"#F0A55A"},"cornerRadius":"soft"}}}'{
"brand": {
"displayName": "Sunny Bakery",
"brandColor": {
"light": "#D9822B",
"dark": "#F0A55A"
},
"typography": {
"bodyEnFontId": "ubuntu",
"bodyZhHantFontId": "noto-sans-tc",
"bodyZhHansFontId": "noto-sans-sc",
"bodyJaFontId": "noto-sans-jp",
"bodyKoFontId": "noto-sans-kr"
},
"cornerRadius": "soft",
"colorScheme": "system"
},
"theme": {
"sectionDensity": "standard"
},
"locale": "zh-Hant",
"currency": "TWD"
}Publish the current draft (all visible pages) so it is live. On first publish you must choose a subdomain (lowercase letters, digits, hyphens; it becomes https://<subdomain>.kanorio.com). Afterwards omit subdomain to keep the current one. Do not use this to change custom domains; use domains.bind. Publishing is rate-limited per token (30/hour). Errors: FORBIDDEN, SUBDOMAIN_MISSING (first publish without subdomain), SUBDOMAIN_TAKEN, RESERVED_SUBDOMAIN, INVALID_SUBDOMAIN, SUBDOMAIN_CHANGE_FORBIDDEN, PLAN_CANNOT_PUBLISH, CONTENT_VALIDATION_FAILED (details.issues lists pages/fields to fix), MODERATION_LOCKED, EMPTY_CONTENT, HOSTNAME_CONFLICT.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
subdomainstringRequired on first publish. Defaults to the site's current subdomain, else its suggestedSubdomain.
Response 200
publishedAtstringrequiredpublicUrlstringrequiredLive URL on kanorio.com.
subdomainstringrequiredcustomDomainStatus"ACTIVE" | "PENDING" | nullrequiredexpectedVersionstring | nullrequiredOpaque content version; the edge may take up to ~30s to serve it.
warningobject | nullrequiredSet when publishing succeeded but Cloudflare sync reported a delay; nothing to retry.
warning.codestringrequiredCONNECTION, DNS_DELAY, SYNC_DELAY, GENERIC_DELAY or GENERIC_SYNC.
warning.messagesstring[]required
Errors
FORBIDDEN403SUBDOMAIN_MISSING422 · first publish without subdomainSUBDOMAIN_TAKEN409RESERVED_SUBDOMAIN422INVALID_SUBDOMAIN400SUBDOMAIN_CHANGE_FORBIDDEN403PLAN_CANNOT_PUBLISH403CONTENT_VALIDATION_FAILED422 · details.issues lists pages/fields to fixMODERATION_LOCKED403EMPTY_CONTENT422HOSTNAME_CONFLICT409
MCP tool site_publish
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/publish" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"subdomain":"sunny-bakery"}'{
"publishedAt": "2026-10-06T08:00:00.000Z",
"publicUrl": "https://sunny-bakery.kanorio.com",
"subdomain": "sunny-bakery",
"customDomainStatus": "ACTIVE",
"expectedVersion": "v_example01",
"warning": null
}Take the website offline (content and settings are kept; publish again to restore). Only the website owner may do this. Errors: FORBIDDEN (also when the website does not exist), OWNER_REQUIRED, WEBSITE_NOT_FOUND.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
unpublishedtruerequired
Errors
FORBIDDEN403 · also when the website does not existOWNER_REQUIRED403WEBSITE_NOT_FOUND404
MCP tool site_unpublish
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/unpublish" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'{
"unpublished": true
}List the origins allowed to call the Content Delivery API cross-origin (CORS allowlist). Same-origin and localhost need no entry. Errors: FORBIDDEN.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Response 200
originsstring[]required
Errors
FORBIDDEN403
MCP tool site_get_allowed_origins
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/allowed-origins" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"origins": [
"https://app.example.com"
]
}Update allowed origins
/sites/{id}/allowed-originsReplace the CORS allowlist for the Content Delivery API (up to 20 https origins such as https://app.example.com; no paths or wildcards). Errors: FORBIDDEN, VALIDATION (details.issues).
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
originsstring[]required
Response 200
originsstring[]required
Errors
FORBIDDEN403VALIDATION400 · details.issues
MCP tool site_update_allowed_origins
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/allowed-origins" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"origins":["https://app.example.com","https://preview.example.com"]}'{
"origins": [
"https://app.example.com",
"https://preview.example.com"
]
}Update developer settings
/sites/{id}/developer-settingsUpdate frontend mode and headless developer settings for the website: set frontendMode (HOSTED or HEADLESS; HEADLESS enables the Content Delivery API and blog publishing without publishing the hosted template site and switches the backoffice to the backend-only view) and/or set the external storefront URL (so visitor traffic on the hosted site 302-redirects to your external frontend while /api/v1/*, /checkout and /account stay active). Pass null to externalStorefrontUrl to clear it. Returns the site's permanent systemHost (s-xxx.kanorio.com). Setting or changing externalStorefrontUrl also prepares the IndexNow key (served by the SDK edge middleware or seoRewrites at /<key>.txt), re-aligns the Search Console property when Search Console is connected (see seo.search_console.status), and restarts the post-launch SEO reminders from that moment. Pass externalListings (HEADLESS only; replaces the whole set) to declare which listing pages your frontend implements: blogIndex, blogCategory, blogTag, blogAuthor, shopIndex, productCategory. Undeclared listings are left out of sitemap.xml, IndexNow and llms.txt so crawlers are never sent to pages your frontend does not serve; none are declared by default, and switching back to HOSTED clears them. Setting frontendMode to HEADLESS (or sending externalStorefrontUrl, which implies HEADLESS) is rejected with HEADLESS_ACCESS_REQUIRED until this website's headless access is approved in the dashboard. Switching back to HOSTED, and updating the external URL while already HEADLESS, does not require approval. Errors: FORBIDDEN, VALIDATION (details.issues), HEADLESS_ACCESS_REQUIRED.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
frontendMode"HOSTED" | "HEADLESS"Frontend mode: HOSTED (Kanorio-hosted pages) or HEADLESS (custom frontend; service availability is always on).
externalStorefrontUrlstring | nullexternalListings"blogIndex" | "blogCategory" | "blogTag" | "blogAuthor" | "shopIndex" | "productCategory"[]Listing pages your external frontend implements (replaces the whole set; pass [] to declare none). HEADLESS only.
Response 200
systemHandlestringrequiredPermanent system handle (s-<base32>) assigned to the website.
systemHoststringrequiredSystem hostname (s-<base32>.kanorio.com) that always serves the Content Delivery API, /checkout and /account.
frontendMode"HOSTED" | "HEADLESS"requiredFrontend mode of the website: HOSTED (Kanorio renders pages) or HEADLESS (custom frontend; backoffice hides hosted-only surfaces and service endpoints are always active).
externalStorefrontUrlstring | nullrequiredExternal frontend origin (e.g. https://www.example.com). When set, hosted pages 302-redirect there while /api/v1/*, /checkout and /account stay served.
externalListings"blogIndex" | "blogCategory" | "blogTag" | "blogAuthor" | "shopIndex" | "productCategory"[]requiredKanorio-managed listing pages your external frontend implements (at the paths from the site's permalink settings). Only declared listings appear in sitemap.xml, IndexNow notifications and llms.txt; individual posts and products are always included. Only applies when frontendMode is HEADLESS.
allowedOriginsstring[]required
Errors
FORBIDDEN403VALIDATION400 · details.issuesHEADLESS_ACCESS_REQUIRED403
MCP tool site_update_developer_settings
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/developer-settings" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"frontendMode":"HEADLESS","externalStorefrontUrl":"https://www.sunnybakery.example","externalListings":["blogIndex","blogCategory"]}'{
"systemHandle": "s-k4m9x2p7q8w3",
"systemHost": "s-k4m9x2p7q8w3.kanorio.com",
"frontendMode": "HEADLESS",
"externalStorefrontUrl": "https://www.sunnybakery.example",
"externalListings": [
"blogIndex",
"blogCategory"
],
"allowedOrigins": [
"https://www.sunnybakery.example"
]
}Update brand profile
/sites/{id}/brand-profileUse site.report_brand_memory_issue instead.
[Deprecated, removed after 2027-04-26; use site.report_brand_memory_issue instead. Direct edits overwrite brand memory. Report what is wrong and Kanorio rewrites it from the site.] Deprecated. Use site.report_brand_memory_issue. Store a declared brand profile (positioning, targetAudience, toneAndStyle, keyOfferings, valueProposition). Declared fields only fill gaps the model left empty; they no longer overwrite generated brand memory. Errors: FORBIDDEN, VALIDATION.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
positioningstringBrand positioning and differentiator.
targetAudiencestringPrimary target audience and use case.
toneAndStylestringVoice, tone, and writing style.
keyOfferingsstring[]Up to 8 core products, services, or content topics.
valuePropositionstringOne-sentence value proposition.
Response 200
positioningstringtargetAudiencestringtoneAndStylestringkeyOfferingsstring[]valuePropositionstringupdatedAtstringrequired
Errors
FORBIDDEN403VALIDATION400
MCP tool site_update_brand_profile
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/brand-profile" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"positioning":"Artisanal sourdough bakery using local heritage grains","targetAudience":"Neighborhood families and specialty coffee lovers","toneAndStyle":"Warm, craft-focused, concise","keyOfferings":["Country sourdough loaves","Seasonal fruit danishes","Weekend baking workshops"]}'{
"positioning": "Artisanal sourdough bakery using local heritage grains",
"targetAudience": "Neighborhood families and specialty coffee lovers",
"toneAndStyle": "Warm, craft-focused, concise",
"keyOfferings": [
"Country sourdough loaves",
"Seasonal fruit danishes",
"Weekend baking workshops"
],
"updatedAt": "2026-10-06T08:00:00.000Z"
}Report brand memory issue
/sites/{id}/brand-memory/feedbackTell Kanorio that one part of the brand memory is wrong. The note is stored as data, not pasted into the memory. Kanorio rewrites that part from the website. field is one of positioning, targetAudience, toneAndStyle, keyOfferings, valueProposition. Errors: FORBIDDEN, VALIDATION.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
field"positioning" | "targetAudience" | "toneAndStyle" | "keyOfferings" | "valueProposition"requirednotestringrequiredWhat is inaccurate. Not executed as an instruction.
Response 201
fieldstringrequiredappliedbooleanrequiredTrue when the memory was rewritten immediately.
createdAtstringrequired
Errors
FORBIDDEN403VALIDATION400
MCP tool site_report_brand_memory_issue
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/brand-memory/feedback" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"field":"keyOfferings","note":"We do not run workshops. The main product is country sourdough loaves."}'{
"field": "keyOfferings",
"applied": true,
"createdAt": "2026-10-06T08:00:00.000Z"
}Mint a read-only headless preview token (knr_pv_…) for reading unpublished drafts via the Content Delivery API (send X-Kanorio-Preview: knr_pv_… or Authorization: Bearer knr_pv_… with ?preview=1). Unlike a PAT, a preview token can only read drafts of this website and expires automatically. Errors: FORBIDDEN, VALIDATION.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
labelstring | nullOptional label (e.g. 'Next.js Draft Mode').
ttlSecondsintegerToken lifetime in seconds (60 to 2592000 = 30 days; default 604800 = 7 days).
Response 200
idstringrequiredtokenstringrequiredRead-only preview token (knr_pv_…). Shown only once.
labelstring | nullrequiredcreatedAtstringrequiredexpiresAtstringrequired
Errors
FORBIDDEN403VALIDATION400
MCP tool site_create_preview_token
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/preview-tokens" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"label":"Next.js Draft Mode","ttlSeconds":604800}'{
"id": "pv_01j8x9k2",
"token": "knr_pv_cm9example0001website.1761652800.pv_01j8x9k2.sig_example",
"label": "Next.js Draft Mode",
"createdAt": "2026-10-06T08:00:00.000Z",
"expiresAt": "2026-10-06T08:00:00.000Z"
}Check external frontend
/sites/{id}/external-frontend/checkRun the External Frontend Doctor against the website's externalStorefrontUrl: probes /sitemap.xml, /robots.txt (including AI crawler rules), /llms.txt, /feed.xml, Schema.org JSON-LD @graph, published page content sync, 24h pageview reporting, and active webhook revalidation endpoints. Pass switching: true to also evaluate pre-/post-switch cutover checks (hosted 302 redirect, /api/v1/redirects rules, Search Console property alignment, Customer API CORS allowlist, and payment return URLs). Call this after deploying or updating an external Next.js / Astro frontend to verify that every integration is green. Errors: FORBIDDEN.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
switchingboolean · queryrequiredWhen true, include switching cutover checks (hosted 302 redirect, redirects, GSC, customer auth CORS, payment return URLs).
Response 200
externalStorefrontUrlstring | nullrequiredoverallStatus"pass" | "warn" | "fail" | "not_configured"requiredcheckedAtstringrequiredchecksobject[]requiredchecks[].id"derivatives" | "jsonld" | "content" | "analytics" | "revalidate" | "indexnow" | "i18n_pages" | "i18n_alternates" | "i18n_files"requiredi18n_* checks appear only on multilingual sites: secondary-locale pages under their URL prefix, hreflang alternates, and per-locale llms.txt.
checks[].titlestringrequiredchecks[].status"pass" | "warn" | "fail"requiredchecks[].summarystringrequiredchecks[].remediationstringrequiredchecks[].detailsobjectswitchingChecksobject[]switchingChecks[].id"hosted_redirect" | "redirects" | "gsc" | "customer_auth" | "payment_return"requiredswitchingChecks[].titlestringrequiredswitchingChecks[].status"pass" | "warn" | "fail"requiredswitchingChecks[].summarystringrequiredswitchingChecks[].remediationstringrequiredswitchingChecks[].detailsobject
Errors
FORBIDDEN403
MCP tool site_check_external_frontend
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/external-frontend/check" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"externalStorefrontUrl": "https://www.sunnybakery.example",
"overallStatus": "pass",
"checkedAt": "2026-10-06T08:00:00.000Z",
"checks": [
{
"id": "derivatives",
"title": "SEO & AEO derivative files (sitemap, robots, llms, feed)",
"status": "pass",
"summary": "sitemap.xml, robots.txt, llms.txt and feed.xml are live and signed by Kanorio.",
"remediation": "Add seoRewrites({ siteUrl, framework: \"next\" | \"astro\" }) from @kanorio/sdk."
},
{
"id": "jsonld",
"title": "Schema.org JSON-LD structured data",
"status": "pass",
"summary": "JSON-LD @graph matched on all 3 sampled page(s).",
"remediation": "Fetch GET /api/v1/seo/jsonld?path=... and render it inside <script type=\"application/ld+json\">."
},
{
"id": "content",
"title": "Published page content sync",
"status": "pass",
"summary": "All 3 sampled page(s) match published titles and content.",
"remediation": "Ensure your frontend reads GET /api/v1/pages/:slug."
},
{
"id": "analytics",
"title": "Visitor pageview reporting (last 24h)",
"status": "pass",
"summary": "Received 42 pageview(s) from the external storefront in the last 24 hours.",
"remediation": "Mount <KanorioAnalytics /> from @kanorio/elements."
},
{
"id": "revalidate",
"title": "On-demand ISR cache revalidation webhook",
"status": "pass",
"summary": "1 active webhook endpoint(s) subscribed for cache invalidation.",
"remediation": "Register a webhook endpoint in Settings → Webhooks."
},
{
"id": "indexnow",
"title": "IndexNow key verification file (/<key>.txt)",
"status": "pass",
"summary": "The IndexNow key file is reachable and matches, so search engines can verify new or updated content.",
"remediation": "Add createKanorioMiddleware({ siteUrl }) from @kanorio/sdk/edge."
}
]
}List recent tenant operational audit log activity on the website (dashboard actions, Management API/MCP writes, page edits, publishes, product updates, domain bindings, and site automations; platform database migrations and initial template seeding are not logged here). Filter by tokenId to inspect what a specific API token or authorized app changed. Newest first. Errors: FORBIDDEN, INVALID_CURSOR.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
tokenIdstring · queryOnly return actions performed by this API token id.
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[].idstringrequiredAudit log entry id.
data[].actionstringrequiredAudit action identifier (e.g. page.sections_replace, website.publish).
data[].resourceTypestringrequiredAffected resource type.
data[].resourceIdstring | nullrequiredAffected resource id.
data[].actorTokenIdstring | nullrequiredAPI token id when performed via MCP or REST API; null for dashboard actions.
data[].summaryobject | nullrequiredStructured non-PII action metadata.
data[].createdAtstringrequiredISO timestamp.
hasMorebooleanrequiredTrue 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 site_activity
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/activity?limit=20" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"data": [
{
"id": "aud_example01",
"action": "page.sections_replace",
"resourceType": "page",
"resourceId": "pg_example456",
"actorTokenId": "tok_example01",
"summary": {
"channel": "mcp"
},
"createdAt": "2026-10-06T08:00:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}