Kanorio Developers
Back to KanorioAPI keys
OverviewCapabilitiesManagement APIDelivery APICustomer APIWebhooksElementsChangelog
  • Section specs
  • Sites
  • Site design & settings
  • Header, footer & site buttons
  • Pages
  • Images
  • Blog
  • Collections
  • Forms
  • Contacts
  • Products
  • Deliverables & access
  • Orders
  • Campaigns
  • Analytics
  • Publishing
  • Domains
  • Error codes

API Reference

Beta

Use an API key to work with your Kanorio sites over HTTPS. This page lists every endpoint's parameters, examples and required permission. The same content is available as a machine-readable OpenAPI spec.

The Agent API is in Beta. Endpoints may change in breaking ways; changes are announced in the changelog first. After general availability, breaking changes go through a deprecation period of at least 6 months.

Before you start

  • Create an API key in Account Settings and choose Other tools.
  • Send Authorization: Bearer knr_pat_your_key with every request.
  • Changes to pages, header, footer and site design are saved as drafts. Call publish to make them live.
  • To read published collection items on the frontend without a key, use the Content Delivery API at https://{your-site}/api/v1/collections/{key}/items.
  • Management API rate limits: 120 requests per minute per key, and 30 publish or unpublish calls per hour. Content Delivery API rate limits (cache hits are unlimited and free; only cache misses and form writes count): 20 requests per second per site (burst 100) and 10 requests per second per visitor IP (burst 30). Over the limit you get a 429; wait for the Retry-After seconds.

Base URL

https://app.kanorio.com/api/v1
REST API guide (opens in a new tab)Download the OpenAPI spec (opens in a new tab)Versioning and change policy (opens in a new tab)Changelog (opens in a new tab)

Endpoint descriptions match what AI assistants read.

Section specs

GET/sections

sections.describe

BetaRead-only

List the section (module) types that can be placed on a page, with an English name, description, category and, in full mode, the JSON Schema (draft 2020-12) each section object must satisfy. Call this before pages.replace_sections. Without `types` you get a compact summary of all types; pass `types` (or detail=full) to receive the schemas. Pass `websiteId` to learn which types are available on that website's plan (sets `available`); this requires the editor:enter scope on that website. Errors: FORBIDDEN (no access to websiteId), WEBSITE_NOT_FOUND.

Permission
Any valid key
Rate limit
120 per minute

Query parameters

  • websiteIdstring

    Optional. When set, `available` reflects that website's plan; needs editor:enter.

  • typesstring[]

    Return only these section types, with full JSON Schemas.

  • detailstring

    summary (default when `types` is omitted) or full (schemas included).

curl
curl -X GET "https://app.kanorio.com/api/v1/sections?types=hero%2Cfaq&detail=summary" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Sites

GET/sites

sites.list

BetaRead-only

List the websites the token owner is a member of, restricted to the websites the token was granted for. Returns ids you need for every other site-scoped call. Errors: none beyond FORBIDDEN when the token lacks console:view.

Permission
console:view
Rate limit
120 per minute
curl
curl -X GET "https://app.kanorio.com/api/v1/sites" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites

sites.create

Beta

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 jobId; poll jobs.get every 5-10 seconds until status is SUCCEEDED (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).

Permission
console:view
Rate limit
120 per minute
curl
curl -X POST "https://app.kanorio.com/api/v1/sites" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
GET/sites/{id}

sites.get

BetaRead-only

Get one website's summary: plan, publish state, subdomain, primary custom domain and whether there are unpublished changes. Errors: FORBIDDEN, WEBSITE_NOT_FOUND.

Permission
console:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/structure

sites.structure

BetaRead-only

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, WEBSITE_NOT_FOUND.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/structure" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/jobs/{id}

jobs.get

BetaRead-only

Get the status of a site-generation job (from sites.create mode=brief) or a page-generation job. Poll until status is SUCCEEDED or FAILED; respect pollAfterSeconds. Errors: JOB_NOT_FOUND, FORBIDDEN.

Permission
console:view
Rate limit
120 per minute

Path parameters

  • idstringRequired
curl
curl -X GET "https://app.kanorio.com/api/v1/jobs/{id}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/search

site.search

BetaRead-only

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. Errors: FORBIDDEN, VALIDATION (details.reason: QUERY_REQUIRED, QUERY_TOO_SHORT or QUERY_TOO_LONG), SEARCH_BUSY.

Permission
console:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • qstringRequired

    Search terms separated by spaces (up to 4 are used). One term needs 2 CJK or Thai characters, or 3 letters or digits.

  • kindstring[]

    Only these kinds. REST accepts kind=PAGE,PRODUCT. Omit for all kinds.

  • limitinteger

    Results to return (1-20, default 20).

curl
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"

Site design & settings

GET/sites/{id}/settings

site.get_settings

BetaRead-only

Read the website's visual theme (brand colors, fonts, corner radius, density, color scheme), content language and website currency. Errors: FORBIDDEN, WEBSITE_NOT_FOUND.

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/settings" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/settings

site.update_settings

Beta

Update the website's visual theme and basic settings; only the fields you send change. brandColor { light, dark } are hex colors used for buttons, links and accents in light/dark mode; brandForeground overrides the text color on brand-colored surfaces (null resets). typography picks body font ids per script (bodyEnFontId, bodyZhHantFontId, bodyZhHansFontId, bodyJaFontId, bodyKoFontId). cornerRadius: sharp | normal | soft; sectionDensity: standard | compact; colorScheme: system | light | dark. 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. Do this before filling pages so the brand colors apply everywhere. Theme changes are draft until site.publish. Errors: FORBIDDEN, WEBSITE_NOT_FOUND, VALIDATION (details lists the failing field), WRITE_FROZEN.

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/settings" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"brandColor":{"light":"#D9822B","dark":"#F0A55A"},"cornerRadius":"soft"}}'

Header, footer & site buttons

GET/sites/{id}/globals

globals.get

BetaRead-only

Read the site-level draft shared by all pages: the primary header and footer (from globalSections), siteSettings (customButtons referenced by sections via ctaIds, socialLinks) and the revision marker contentUpdatedAt. Call it before globals.replace_header / globals.replace_footer / globals.update_site_settings and pass contentUpdatedAt back as ifUnmodifiedSince. Images of the site's media library appear as asset:<assetId>; assets holds them (with display URLs). Errors: FORBIDDEN, WEBSITE_NOT_FOUND.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/globals" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/globals/headers

globals.list_headers

BetaRead-only

List all headers in globalSections: the primary header plus any secondary headers created for specific pages (ADR-0001). Most websites only need the primary header. Errors: FORBIDDEN, WEBSITE_NOT_FOUND.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/globals/headers" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/globals/headers

globals.create_header

Beta

Create a secondary header in globalSections with a human-readable key. Only use this when specific pages need a different navigation bar (for example a sub-brand or campaign microsite); most websites only need the primary header. After creating the header, assign its returned id to a page with pages.update({ globalHeaderId }). Saved as draft only; call site.publish to go live. Errors: FORBIDDEN, WEBSITE_NOT_FOUND, VALIDATION, KEY_TAKEN, CONFLICT, WRITE_FROZEN, ASSET_NOT_FOUND (details.assetIds: not in this site's media library), ASSET_NOT_ALLOWED (product files cannot be shown on pages).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/globals/headers" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ifUnmodifiedSince":"2026-10-06T08:00:00.000Z","key":"campaign","header":{"id":"hdr_campaign_01","type":"header","navItems":[{"id":"nav_home","text":"Home","linkType":"internal","pageSlug":"/"},{"id":"nav_about","text":"About Us","linkType":"internal","pageSlug":"/about"}],"ctaIds":["btn_order"],"key":"campaign"}}'
DELETE/sites/{id}/globals/headers/{key}

globals.delete_header

Beta

Delete a secondary header by key and automatically reassign any pages using it back to the primary header. The primary header cannot be deleted. Saved as draft only; call site.publish to go live. Errors: FORBIDDEN, WEBSITE_NOT_FOUND, HEADER_NOT_FOUND, PRIMARY_HEADER_PROTECTED, CONFLICT, WRITE_FROZEN.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Unique header key (a-z, 0-9, hyphen, underscore; 1-40 chars). 'primary' refers to the main site header.

Query parameters

  • ifUnmodifiedSincestringRequired

    contentUpdatedAt from globals.get. Required: the write is rejected with CONFLICT when the site-level draft changed since (details.contentUpdatedAt holds the current value).

curl
curl -X DELETE "https://app.kanorio.com/api/v1/sites/{id}/globals/headers/{key}?ifUnmodifiedSince=2026-10-06T08%3A00%3A00.000Z" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/globals/site-settings

globals.update_site_settings

Beta

Partially update siteSettings (only the keys you send are replaced; optimistic concurrency). customButtons is the single list of CTA buttons for the whole site: define them here (text, url, optional icon and style), then reference their ids from sections, the header and the footer via ctaIds (up to 2 per place). Buttons without an id get one; read it back from the response. Replaces the whole list when sent. socialLinks: [{ platform, url }] rendered in the footer and social sections. Contact fields (address/phone/email/hours) are business facts and are not editable here. Saved as draft only; call site.publish to go live. Errors: FORBIDDEN, WEBSITE_NOT_FOUND, VALIDATION, CONFLICT, WRITE_FROZEN, ASSET_NOT_FOUND (details.assetIds: not in this site's media library), ASSET_NOT_ALLOWED (product files cannot be shown on pages).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/globals/site-settings" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ifUnmodifiedSince":"2026-10-06T08:00:00.000Z","siteSettings":{"customButtons":[{"id":"btn_order","text":"Order online","url":"/shop","style":"brand"}]}}'
PUT/sites/{id}/globals/header

globals.replace_header

Beta

Replace the primary header or a secondary header identified by key (whole object, optimistic concurrency). The header's id stays the one pages already reference; when the site has no header yet, the given id is used and attached to every page. Typical use after creating pages: rebuild navItems so each visible page has an internal link. Saved as draft only; call site.publish to go live. Reference a logoOverride image as asset:<assetId>. Errors: FORBIDDEN, WEBSITE_NOT_FOUND, HEADER_NOT_FOUND, VALIDATION (details lists the failing field), CONFLICT, WRITE_FROZEN, ASSET_NOT_FOUND (details.assetIds: not in this site's media library), ASSET_NOT_ALLOWED (product files cannot be shown on pages).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/globals/header" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ifUnmodifiedSince":"2026-10-06T08:00:00.000Z","header":{"id":"sec_header01","type":"header","navItems":[{"id":"nav_home","text":"Home","linkType":"internal","pageSlug":"/"},{"id":"nav_about","text":"About Us","linkType":"internal","pageSlug":"/about"}],"ctaIds":["btn_order"]}}'
PUT/sites/{id}/globals/footer

globals.replace_footer

Beta

Replace the primary footer (whole object, optimistic concurrency). The footer's id stays the one pages already reference; when the site has no footer yet, the given id is used and attached to every page. Saved as draft only; call site.publish to go live. Errors: FORBIDDEN, WEBSITE_NOT_FOUND, VALIDATION, CONFLICT, WRITE_FROZEN, ASSET_NOT_FOUND (details.assetIds: not in this site's media library), ASSET_NOT_ALLOWED (product files cannot be shown on pages).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/globals/footer" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ifUnmodifiedSince":"2026-10-06T08:00:00.000Z","footer":{"id":"sec_footer01","type":"footer","copyright":"© 2026 Sunny Bakery"}}'

Pages

GET/sites/{id}/pages

pages.list

BetaRead-only

List the pages of a website (home page first), with slug, visibility, SEO fields and the revision marker updatedAt. Sections are not included; use pages.get. Errors: FORBIDDEN.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/pages" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/pages

pages.create

Beta

Create an empty page (up to 3 levels deep). Pass parentSlug (e.g. "/services") plus the last segment in slug (e.g. "/haircut") rather than concatenating multi-segment paths yourself; if the parent page does not exist yet, a hidden folder page is created automatically. Omit slug to have one generated from the title. The home page ("/") already exists and cannot be created; link-in-bio pages have a dedicated flow. The new page uses the home page's header and footer unless you pass globalHeaderId/globalFooterId. It is not added to the navigation automatically: after creating pages, call globals.replace_header with a navItem per visible page. Fill the page afterwards with pages.replace_sections. Errors: FORBIDDEN, VALIDATION, SLUG_TAKEN, RESERVED_SLUG, RESERVED_SEGMENT, SEGMENT_TOO_LONG, DEPTH_EXCEEDED, PARENT_NOT_ALLOWED, BREADCRUMB_PARENT_INVALID, HOME_NOT_CREATABLE, LINK_IN_BIO_DEDICATED_ENTRY.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/pages" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"About Us","slug":"/about","isVisible":true}'
PUT/sites/{id}/pages/order

pages.reorder

Beta

Set the order of all non-home pages (the home page is always first). Order drives the page list in the console; navigation links come from the header's navItems, which you manage with globals.replace_header. pageIds must list every non-home page exactly once; legal pages created with the site count too (get them from pages.list). Errors: FORBIDDEN, FOREIGN_PAGE (an id is not a page of this site), HOME_NOT_SORTABLE (home page included), INCOMPLETE_ORDER (some pages missing or duplicated).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/pages/order" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"pageIds":["pg_example789"]}'
GET/sites/{id}/pages/{pageId}

pages.get

BetaRead-only

Get one page including its current draft sections, hierarchy metadata (path, parentSlug, isDirectory, depth, ancestors, breadcrumbs, breadcrumbSource) and updatedAt. Always read the page right before pages.replace_sections and pass updatedAt back as ifUnmodifiedSince. Images and files of the site's media library appear as asset:<assetId> in sections; assets holds them (with display URLs). Errors: FORBIDDEN, PAGE_NOT_FOUND.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • pageIdstringRequired

    Page id. Get it from pages.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/pages/{pageId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/pages/{pageId}

pages.update

Beta

Update a page's properties: title, slug, parentSlug, createRedirect, breadcrumbParentSlug, visibility, header/footer reference, SEO fields (seoTitle, seoDescription, seoKeywords, og*, canonicalUrl, robots*) and excludeFromSiteSearch. Only the fields you send change; sections are untouched (use pages.replace_sections). Nullable fields (parentSlug, breadcrumbParentSlug, globalHeaderId, globalFooterId, ogImageAssetId) can be cleared by passing null; all other fields can only be replaced, not cleared. Slug, parentSlug and visibility changes take effect on the published site immediately; renaming automatically updates descendant subpages and creates 301 redirects for previously published pages unless createRedirect is false. Errors: FORBIDDEN, PAGE_NOT_FOUND, PARENT_NOT_FOUND, PARENT_NOT_ALLOWED, DEPTH_EXCEEDED, SEGMENT_TOO_LONG, RESERVED_SLUG, RESERVED_SEGMENT, BREADCRUMB_PARENT_INVALID, REDIRECT_CONFLICT, REDIRECT_LIMIT, VALIDATION, SLUG_TAKEN, HOME_SLUG_IMMUTABLE, HOME_NOT_HIDEABLE, PRODUCT_PAGES_PLAN_REQUIRED, ASSET_NOT_FOUND, ASSET_NOT_ALLOWED.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • pageIdstringRequired

    Page id. Get it from pages.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/pages/{pageId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"seoTitle":"About Us | Sunny Bakery","seoDescription":"Naturally leavened sourdough baked fresh every morning.","ogImageAssetId":"asset_example01"}'
DELETE/sites/{id}/pages/{pageId}

pages.delete

Beta

Delete a page and its draft content. The home page cannot be deleted. When the page has subpages, you must pass mode: "subtree" (delete the page and all its subpages) or "lift-children" (move subpages up one level with 301 redirects before deleting the parent); omitting mode returns HAS_CHILDREN with details.children. Errors: FORBIDDEN, PAGE_NOT_FOUND, HOME_NOT_DELETABLE, HAS_CHILDREN, SLUG_TAKEN, REDIRECT_CONFLICT, REDIRECT_LIMIT.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • pageIdstringRequired

    Page id. Get it from pages.list.

Query parameters

  • modestring

    Required when deleting a parent page that has subpages: "subtree" deletes all descendants; "lift-children" moves subpages up one level first.

  • createRedirectboolean

    With mode "lift-children": whether to create 301 redirects from the old subpage URLs for previously published subpages (defaults to true). Ignored otherwise.

curl
curl -X DELETE "https://app.kanorio.com/api/v1/sites/{id}/pages/{pageId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PUT/sites/{id}/pages/{pageId}/sections

pages.replace_sections

Beta

Replace the whole draft section list of a page (optimistic concurrency). Get the shapes from sections.describe; read the page with pages.get and pass its updatedAt as ifUnmodifiedSince. If someone else (another agent or a person in the editor) changed the page since, the call fails with CONFLICT and details.updatedAt holds the current revision: re-read the page, merge, retry. Changes are saved as draft only; call site.publish to go live. Reference images and files as asset:<assetId> (from media.list, media.upload, media.import_stock, media.upload_link_status); values you read with pages.get can be sent back unchanged. Errors: FORBIDDEN, PAGE_NOT_FOUND, VALIDATION (details lists the failing section index and field), CONFLICT, ASSET_NOT_FOUND (details.assetIds: not in this site's media library), ASSET_NOT_ALLOWED (product files cannot be shown on pages).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • pageIdstringRequired

    Page id. Get it from pages.list.

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/pages/{pageId}/sections" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ifUnmodifiedSince":"2026-10-06T08:00:00.000Z","sections":[{"id":"sec_hero01","type":"hero","title":"Freshly baked sourdough every morning","subtitle":"Natural starter, local grain. Doors open at 7 a.m.","image":"asset:asset_example01","ctaIds":["btn_order"]},{"id":"sec_faq01","type":"faq","title":"Frequently Asked Questions","items":[{"question":"Can I pre-order loaves?","answer":"Yes, place pre-orders by 5 p.m. the day before."}]}]}'
GET/sites/{id}/revisions

revisions.list

BetaRead-only

List content revisions for a website draft (subject: WEBSITE, subjectId omitted or equal to websiteId) or a blog post (subject: BLOG_POST, subjectId: postId). Revisions are retained for 30 days and coalesced within 10-minute autosave windows. Optionally pass pageId when listing WEBSITE revisions to filter to edits that touched that page. Errors: FORBIDDEN.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • subjectstring

    Defaults to WEBSITE.

  • subjectIdstring

    Required when subject is BLOG_POST (pass the postId); defaults to websiteId for WEBSITE.

  • pageIdstring

    Optional filter for WEBSITE revisions: only revisions that modified this page or whole-site checkpoints.

  • limitinteger

    Max revisions to return (1-200, default 50).

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/revisions?subject=WEBSITE&limit=20" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/revisions/{revisionId}

revisions.get

BetaRead-only

Get the full unpacked snapshot of a content revision by revisionId. Media references belonging to this site's library are presented as asset:<assetId> with display metadata in assets. Errors: FORBIDDEN, REVISION_NOT_FOUND.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • revisionIdstringRequired

    Revision identifier from revisions.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/revisions/{revisionId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/revisions/{revisionId}/restore

revisions.restore

Beta

Restore a website draft or blog post working copy to a previous revision. Before writing, the current draft is automatically backed up as a PRE_RESTORE revision so the restore itself can be undone. Restoring writes only to the draft/working copy and never alters the live published site directly; call site.publish or posts.publish to publish. For WEBSITE revisions, pass mode: "page" with pageId to restore only a single page's sections, or mode: "full" (default) to restore the entire site draft and theme settings. Errors: FORBIDDEN, REVISION_NOT_FOUND, SUBJECT_NOT_FOUND, PAGE_NOT_FOUND_IN_REVISION, PAGE_NOT_FOUND_IN_SITE, VALIDATION_FAILED, WRITE_FROZEN.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • revisionIdstringRequired

    Revision identifier from revisions.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/revisions/{revisionId}/restore" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"page","pageId":"pg_example456"}'

Images

GET/sites/{id}/media

media.list

BetaRead-only

List the website's media library (the same files the user sees in the dashboard): images, videos, audio, PDFs, documents and product files, newest first, plus storage usage. Filter with assetKind, unusedOnly and search (file name); pages hold 40 files, so pass offset to continue while hasMore is true. Files in the trash and older versions kept after media.replace are not listed; files in the trash still count toward storage. Errors: FORBIDDEN.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • assetKindstring

    Only this kind of file; all kinds by default.

  • unusedOnlyboolean | string

    Only files no content uses (candidates for cleaning up storage).

  • searchstring

    Part of the file name.

  • sortstring

    newest (default), oldest or largest.

  • offsetinteger

    Number of items to skip.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media?assetKind=image&unusedOnly=false" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/media

media.upload

Beta

Upload one image into the website's media library. Provide exactly one of base64 or url. Files the user attaches in chat are not available to you: use media.create_upload_link for those, media.list_page_images for a page the user owns, or media.search_stock when they have no photos. Returns the asset: put asset.url in sections (pages.replace_sections), blog posts and product images, and keep asset.assetId to manage the file later (media.get, media.replace, media.trash). Uploads are kept; unused files show as unused in the media library. Errors: FORBIDDEN, VALIDATION, INVALID_IMAGE_DATA, UNSUPPORTED_IMAGE_TYPE, FILE_TOO_LARGE, FETCH_FAILED, UPLOAD_FAILED, STORAGE_LIMIT_EXCEEDED (plan storage is full; images count toward it).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/photos/croissant.jpg"}'
POST/sites/{id}/media/upload-links

media.create_upload_link

Beta

Create a short-lived (15 min) upload page the user opens on their phone or computer to send you photos. Use it whenever the user offers photos in the conversation: chat clients never pass attachments to tools, so you cannot see or upload those files yourself and must not pretend you did. Reply to the user with the returned url in plain words (e.g. 'Open this link and add your photos, then tell me when you're done'). Afterwards call media.upload_link_status with the same token to get the uploaded images. Errors: FORBIDDEN, SERVICE_UNAVAILABLE (try again shortly).

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/upload-links" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"maxFiles":6,"purpose":"menu photos"}'
GET/sites/{id}/media/upload-links/status

media.upload_link_status

BetaRead-only

Check what the user has uploaded through an upload link (media.create_upload_link). Call it when the user says they are done, or to poll every 20-30 seconds while waiting. Returns the uploaded images as assets (put asset.url in sections, keep assetId to manage them) and how many more the link accepts. The status stays readable for a few minutes after the link expires. Errors: FORBIDDEN, LINK_INVALID (token does not belong to this website or is malformed).

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • tokenstringRequired

    The token returned by media.create_upload_link.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media/upload-links/status?token=example-upload-link-token" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/media/stock

media.search_stock

BetaRead-only

Search free stock photos (Pexels and Pixabay) when the user has no photos of their own. Ask the user first whether they have photos; use stock as the fallback and tell them the images are stock photos they can replace later. Write the query in English with concrete nouns (e.g. 'sourdough bread bakery counter', not 'nice bakery'); add orientation=landscape for hero backgrounds. Both libraries allow commercial use without attribution; do not use stock photos as the brand logo or to imply endorsement. Pick a hit, then call media.import_stock to copy it into the website's media library. Errors: FORBIDDEN, SEARCH_BUSY (retry after details.retryAfter seconds), SEARCH_FAILED, UNKNOWN_PROVIDER.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

    Website the photos are for (scopes the token's access).

Query parameters

  • querystringRequired

    English search terms work best.

  • orientationstring
  • providerstring

    Defaults to all (results interleaved).

  • perPageinteger

    Defaults to 9.

  • pageinteger
curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media/stock?query=sourdough+bread+bakery+counter&orientation=landscape&perPage=3" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/media/stock

media.import_stock

Beta

Copy one stock photo from media.search_stock into the website's media library. Pass provider, id and downloadURL exactly as returned by the search. Returns the asset: put asset.url in sections (backgroundImage, images[].url) and keep asset.assetId to manage it. Write the imageAlt yourself in the site's content language, based on the photo's alt and the section's purpose. Errors: FORBIDDEN, UNKNOWN_PROVIDER, INVALID_STOCK_URL, STOCK_BUSY, INVALID_IMAGE_DATA, UPLOAD_FAILED, STORAGE_LIMIT_EXCEEDED.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/stock" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"pexels","id":"1234567","downloadURL":"https://images.pexels.com/photos/1234567/pexels-photo-1234567.jpeg"}'
GET/sites/{id}/media/page-images

media.list_page_images

BetaRead-only

List the images found on a public web page (its cover image, content images and logo) when the user points you to a site they own, such as their old website or a brand page. Nothing is downloaded; pick the ones you need and copy them with media.upload (url mode). Before copying, confirm with the user that the images are theirs or they have the right to use them; do not copy images from sites that are not the user's. Pages behind a login or anti-bot walls (Instagram, Facebook) fail with FETCH_FAILED: ask the user to use an upload link instead. Errors: FORBIDDEN, INVALID_URL (private or non-http address), FETCH_FAILED (details.status is the upstream status).

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

    Website the images are for (scopes the token's access).

Query parameters

  • urlstringRequired
  • maxinteger

    Defaults to 12.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media/page-images?url=https%3A%2F%2Fold-site.example.com%2F&max=5" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/media/{assetId}

media.get

BetaRead-only

Get one file from the media library or the trash, with every place the site uses it (page sections, share images, brand, posts, products, emails). Errors: FORBIDDEN, ASSET_NOT_FOUND.

Permission
editor:enter
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • assetIdstringRequired

    Asset id from media.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
DELETE/sites/{id}/media/{assetId}

media.purge

Beta

Permanently delete a file that is already in the trash (call media.trash first) and free its storage. This cannot be undone. If the site still uses the file, the call fails with ASSET_IN_USE and details.usages: tell the user those places will stay empty, and only retry with confirm: true after they agree. Errors: FORBIDDEN, NOT_FOUND, NOT_IN_TRASH, PRODUCT_FILE_PROTECTED, ASSET_IN_USE, EXPORT_HOLD (details.until: an export keeps the file until then), DELETE_FAILED.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • assetIdstringRequired

    Asset id from media.list.

Query parameters

  • confirmboolean | string

    Set true (REST: ?confirm=true) only after the user agreed that the places in ASSET_IN_USE details.usages will stay empty.

curl
curl -X DELETE "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/media/{assetId}/replace

media.replace

Beta

Upload a new version of an image (exactly one of base64 or url). Every place that uses it switches to the new version and keeps the same assetId, so no content needs to be edited: the editor, brand logo, share images, products and blog posts right away; published pages after the next site.publish (awaitingPublish). When the aspect ratio changes, crops are re-centered per layout (aspectRatioChanged): tell the user to check the pages listed in usages. Tell the user before replacing a file that many places use. Only images; videos, audio, PDFs and documents are replaced in the dashboard media library. The older version stays until no published page uses it, then moves to the trash. Errors: FORBIDDEN, ASSET_NOT_FOUND (also for files in the trash), IMAGES_ONLY, REPLACE_NOT_AVAILABLE, VALIDATION, INVALID_IMAGE_DATA, UNSUPPORTED_IMAGE_TYPE, FILE_TOO_LARGE, FETCH_FAILED, STORAGE_LIMIT_EXCEEDED, SERVICE_UNAVAILABLE, UPLOAD_FAILED.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • assetIdstringRequired

    Asset id from media.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}/replace" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/photos/hero-2026.jpg"}'
POST/sites/{id}/media/{assetId}/trash

media.trash

Beta

Move a file to the trash. It stops showing on the site right away (places that use it show nothing), still counts toward storage, and is deleted permanently after 30 days unless restored with media.restore. The response lists where it was used so you can tell the user what will look empty. Product files cannot be trashed; remove them from the product instead. Errors: FORBIDDEN, NOT_FOUND, PRODUCT_FILE_PROTECTED.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • assetIdstringRequired

    Asset id from media.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}/trash" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
POST/sites/{id}/media/{assetId}/restore

media.restore

Beta

Restore a file from the trash; it shows on the site again right away. An older version left by replacing a file comes back as a separate file with a new assetId (returned); old content keeps showing the current version. Errors: FORBIDDEN, NOT_FOUND, PRODUCT_FILE_PROTECTED, RESTORE_FAILED.

Permission
content:edit
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • assetIdstringRequired

    Asset id from media.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}/restore" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Blog

GET/sites/{id}/posts

posts.list

BetaRead-only

List blog posts (drafts and published) with status, taxonomy and revision timestamps; contentMarkdown is not included, use posts.get. Filter by status, category, tag or search; sort by edited/published/views. Errors: FORBIDDEN.

Permission
posts:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • statusstring
  • searchstring

    Case-insensitive match on title or slug.

  • categoryIdstring
  • tagIdstring
  • sortstring

    Defaults to edited_desc.

  • limitinteger

    Page size (1-100). Defaults vary per capability.

  • offsetinteger

    Number of items to skip.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/posts?status=DRAFT" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/posts

posts.create

Beta

Create a blog post as a DRAFT. Drafts are not visible on the public site until posts.publish. Taxonomy ids (categoryIds, tagIds, authorIds) must belong to this website; use blog_categories.list / blog_tags.list first. Errors: FORBIDDEN, VALIDATION, SLUG_TAKEN, ASSET_NOT_FOUND (details.assetIds: not in this site's media library; the share image also must not be in the trash), ASSET_NOT_ALLOWED (product files, or a non-image as the share image).

Permission
blog:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/posts" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"How we feed our sourdough starter","contentMarkdown":"## 5 a.m. in the bakehouse\n\nDiscard half the starter, then fold in fresh flour and warm water...","categoryIds":["cat_example01"],"tagIds":["tag_example01"]}'
GET/sites/{id}/posts/{postId}

posts.get

BetaRead-only

Get one blog post including the working-copy contentMarkdown (what the editor shows, not necessarily the published copy) and SEO fields. Errors: FORBIDDEN, POST_NOT_FOUND.

Permission
posts:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • postIdstringRequired

    Blog post id. Get it from posts.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/posts/{postId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/posts/{postId}

posts.update

Beta

Partially update a blog post's working copy (only provided fields change; categoryIds/tagIds/authorIds replace the whole list when provided). Editing a PUBLISHED post does not change the public copy until posts.publish is called again (hasDraftChanges becomes true). Errors: FORBIDDEN, POST_NOT_FOUND, VALIDATION, SLUG_TAKEN, ASSET_NOT_FOUND (details.assetIds: not in this site's media library; the share image also must not be in the trash), ASSET_NOT_ALLOWED (product files, or a non-image as the share image).

Permission
blog:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • postIdstringRequired

    Blog post id. Get it from posts.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/posts/{postId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"excerpt":"The first thing we do at 5 a.m. every morning."}'
POST/sites/{id}/posts/{postId}/publish

posts.publish

Beta

Publish a blog post (or re-publish a published post's pending edits). Snapshots the working copy to the public site immediately; the website itself must already be published (site.publish) and on a paid plan. Errors: FORBIDDEN, POST_NOT_FOUND, PRO_REQUIRED, SITE_NOT_PUBLISHED, SLUG_TAKEN.

Permission
blog:manage
Rate limit
30 per hour

Path parameters

  • idstringRequired

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

  • postIdstringRequired

    Blog post id. Get it from posts.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/posts/{postId}/publish" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
POST/sites/{id}/posts/{postId}/unpublish

posts.unpublish

Beta

Revert a published post to DRAFT so it disappears from the public site. The content is kept; publishedAt is preserved as the first-publish date. Errors: FORBIDDEN, POST_NOT_FOUND.

Permission
blog:manage
Rate limit
30 per hour

Path parameters

  • idstringRequired

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

  • postIdstringRequired

    Blog post id. Get it from posts.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/posts/{postId}/unpublish" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
GET/sites/{id}/blog/categories

blog_categories.list

BetaRead-only

List this website's blog categories (id, slug, name, counts.posts). Use the ids in posts.create / posts.update categoryIds and primaryCategoryId. Errors: FORBIDDEN.

Permission
posts:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/blog/categories" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/blog/tags

blog_tags.list

BetaRead-only

List this website's blog tags (id, slug, name, counts.posts). Use the ids in posts.create / posts.update tagIds. Errors: FORBIDDEN.

Permission
posts:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/blog/tags" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Collections

GET/sites/{id}/collections

collections.list

BetaRead-only

List the site's collections (custom and system) with their field schemas and item counts. Call this before reading or writing items. Errors: FORBIDDEN.

Permission
collections:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/collections" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/collections

collections.create

Beta

Create a collection (a small database for site content: teachers, projects, locations, FAQ…). Not for app data such as members, orders or comments. Field types: text, rich_text, email, phone, url, video_url, number, boolean, date, datetime, select, multi_select, color, image, gallery, file, reference, multi_reference, json. Every field needs id (snake_case), name, type; required defaults to false. Constraints: text min/max/pattern/unique/default; number integer/min/max/unique; select/multi_select options [{id,label}]; reference/multi_reference targetCollectionKey; gallery/multi_* max. Add aiHint to tell future agents how to fill a field. titleFieldId must point to a text/email/url field; it names items and generates slugs. Returns the Delivery API endpoints and a fetch example. Errors: FORBIDDEN, VALIDATION (details.issues), KEY_TAKEN, COLLECTION_LIMIT_REACHED, SYSTEM_COLLECTION (reserved key).

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/collections" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key":"teachers","displayName":"Teachers","singularName":"Teacher","description":"Faculty shown on the About page.","titleFieldId":"name","fields":[{"id":"name","name":"Name","type":"text","required":true,"max":80,"unique":true,"aiHint":"Full name as shown on the site."},{"id":"avatar","name":"Avatar","type":"image","required":false,"help":"Square portrait."},{"id":"subjects","name":"Subjects","type":"multi_select","required":true,"options":[{"id":"math","label":"Math"},{"id":"english","label":"English"},{"id":"physics","label":"Physics"}]},{"id":"bio","name":"Bio","type":"rich_text","required":false,"max":2000},{"id":"website","name":"Personal website","type":"url","required":false},{"id":"course","name":"Main course","type":"reference","required":false,"targetCollectionKey":"courses"}]}'
GET/sites/{id}/collections/{key}

collections.get

BetaRead-only

Get one collection's schema (fields, constraints, aiHint) plus its Content Delivery API endpoints. Errors: FORBIDDEN, COLLECTION_NOT_FOUND.

Permission
collections:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/collections/{key}

collections.update

Beta

Change a collection's name, description, titleFieldId or fields. fields replaces the whole ordered list: read it with collections.get, edit, send it back. Allowed immediately: add fields, rename, change help/aiHint, add options, relax constraints, remove custom fields (data is ignored, cleaned on next write). Type changes only along compatible paths (e.g. text→rich_text, select→multi_select, reference→multi_reference, image→gallery); otherwise add a new field, migrate, remove the old one. Tightened constraints are checked against existing items first. System collections (blog): reserved fields cannot be removed or retyped. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, VALIDATION (details.issues), SCHEMA_CHANGE_REJECTED (details.schemaIssues).

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description":"Faculty and guest lecturers.","fields":[{"id":"name","name":"Name","type":"text","required":true,"max":80,"unique":true,"aiHint":"Full name as shown on the site."},{"id":"avatar","name":"Avatar","type":"image","required":false,"help":"Square portrait."},{"id":"subjects","name":"Subjects","type":"multi_select","required":true,"options":[{"id":"math","label":"Math"},{"id":"english","label":"English"},{"id":"physics","label":"Physics"}]},{"id":"bio","name":"Bio","type":"rich_text","required":false,"max":2000},{"id":"website","name":"Personal website","type":"url","required":false},{"id":"course","name":"Main course","type":"reference","required":false,"targetCollectionKey":"courses"},{"id":"years","name":"Years of experience","type":"number","required":false,"integer":true,"min":0}]}'
DELETE/sites/{id}/collections/{key}

collections.delete

Beta

Delete a custom collection and ALL its items. Irreversible. confirmKey must equal key. Refused while another collection references it, and for system collections. Not included in any OAuth scope preset; a PAT must grant collections:manage explicitly. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, CONFIRM_REQUIRED, REFERENCED (details.referencedBy), SYSTEM_COLLECTION.

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

Query parameters

  • confirmKeystringRequired

    Must equal key.

curl
curl -X DELETE "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}?confirmKey=teachers" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/collections/{key}/items

items.list

BetaRead-only

List or query items with the same filter/sort DSL the Content Delivery API uses: AND of up to 10 conditions, 3 sort fields, cursor or offset paging. source=draft (default) includes unpublished edits; source=published returns only what the public API serves. Over REST (GET) pass filter and sort as JSON strings, e.g. ?filter={"subjects":{"has":"math"}}&sort=-publishedAt. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, VALIDATION (details.queryIssues: unknown field, op not allowed for the type, invalid value).

Permission
collections:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

Query parameters

  • sourcestring
  • statusstring

    Only with source=draft.

  • filterobject | string
  • sortstring | object[]

    Array of { field, direction } or a comma-separated shorthand, e.g. "-publishedAt,name".

  • limitinteger

    Page size (1-100). Defaults vary per capability.

  • offsetinteger

    Number of items to skip.

  • cursorstring
  • fieldsstring[]

    Only return these fieldIds.

  • expandstring[]

    Reference fields to expand one level (id, slug, title).

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items?source=published&filter=%7B%22subjects%22%3A%7B%22has%22%3A%22math%22%7D%2C%22name%22%3A%7B%22contains%22%3A%22chen%22%7D%7D&sort=-publishedAt%2Cname&limit=20" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/collections/{key}/items

items.create

Beta

Create one item. Values are validated against the collection schema; fix the listed fieldIds on VALIDATION. slug is generated from the title field when omitted. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, VALIDATION (details.issues: fieldId, code, message, example), UNIQUE_CONFLICT, SLUG_TAKEN, PLAN_ITEM_LIMIT_REACHED (details.quota), COLLECTION_ITEM_LIMIT_REACHED, SYSTEM_COLLECTION (blog collections are read-only here; use blog.*).

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fieldData":{"name":"Amy Chen","avatar":"asset_example01","subjects":["math","physics"],"bio":"Ten years of teaching high-school math.","website":"https://amychen.example.com","course":"item_example02"},"publish":true}'
POST/sites/{id}/collections/{key}/items/bulk

items.bulk_create

Beta

Create up to 200 items in one transaction (all or nothing). On failure nothing is written and failed[] lists each bad entry by index with its issues. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, VALIDATION, UNIQUE_CONFLICT, SLUG_TAKEN, PLAN_ITEM_LIMIT_REACHED (details.quota), COLLECTION_ITEM_LIMIT_REACHED, SYSTEM_COLLECTION.

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/bulk" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"fieldData":{"name":"Amy Chen","avatar":"asset_example01","subjects":["math","physics"],"bio":"Ten years of teaching high-school math.","website":"https://amychen.example.com","course":"item_example02"}},{"fieldData":{"name":"Ben Lin","subjects":["english"]}}],"publish":true}'
PATCH/sites/{id}/collections/{key}/items/bulk

items.bulk_update

Beta

Update up to 200 items in one transaction (all or nothing); each entry is a partial update by itemId. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, ITEM_NOT_FOUND, VALIDATION, UNIQUE_CONFLICT, SLUG_TAKEN, SYSTEM_COLLECTION.

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/bulk" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"itemId":"item_example01","fieldData":{"subjects":["math"]}}],"publish":true}'
POST/sites/{id}/collections/{key}/items/publish

items.publish

Beta

Publish up to 200 items: copies each draft to the public copy. Returns the updated items and ids that were not found. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, VALIDATION, SYSTEM_COLLECTION.

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/publish" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"itemIds":["item_example01"]}'
POST/sites/{id}/collections/{key}/items/unpublish

items.unpublish

Beta

Unpublish up to 200 items: removes them from the Content Delivery API; drafts are kept. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, VALIDATION, SYSTEM_COLLECTION.

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/unpublish" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"itemIds":["item_example01"]}'
GET/sites/{id}/collections/{key}/items/{itemId}

items.get

BetaRead-only

Get one item by id or slug, with draft and published values. Errors: FORBIDDEN, COLLECTION_NOT_FOUND, ITEM_NOT_FOUND.

Permission
collections:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

  • itemIdstringRequired

    Item id or slug.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/{itemId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/collections/{key}/items/{itemId}

items.update

Beta

Partially update an item: only the fieldIds you send change; send null to clear a field. publish=false keeps the public copy unchanged (status becomes changed). Errors: FORBIDDEN, COLLECTION_NOT_FOUND, ITEM_NOT_FOUND, VALIDATION (details.issues: fieldId, code, message, example), UNIQUE_CONFLICT, SLUG_TAKEN, PLAN_ITEM_LIMIT_REACHED (details.quota), COLLECTION_ITEM_LIMIT_REACHED, SYSTEM_COLLECTION (blog collections are read-only here; use blog.*).

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

  • itemIdstringRequired

    Item id. Get it from items.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/{itemId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fieldData":{"bio":"Updated bio, not yet published."},"publish":false}'
DELETE/sites/{id}/collections/{key}/items/{itemId}

items.delete

Beta

Delete one item. Refused with REFERENCED (details.referencedBy) while other items reference it, unless force=true (readers then ignore the dangling id). Errors: FORBIDDEN, COLLECTION_NOT_FOUND, ITEM_NOT_FOUND, REFERENCED, SYSTEM_COLLECTION.

Permission
collections:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • keystringRequired

    Collection key (slug). Get it from collections.list.

  • itemIdstringRequired

    Item id. Get it from items.list.

Query parameters

  • forceboolean
curl
curl -X DELETE "https://app.kanorio.com/api/v1/sites/{id}/collections/{key}/items/{itemId}?force=false" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/allowed-origins

site.get_allowed_origins

BetaRead-only

List the origins allowed to call the Content Delivery API cross-origin (CORS allowlist). Same-origin and localhost need no entry. Errors: FORBIDDEN.

Permission
collections:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/allowed-origins" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PUT/sites/{id}/allowed-origins

site.update_allowed_origins

Beta

Replace 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).

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
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"]}'
PATCH/sites/{id}/developer-settings

site.update_developer_settings

Beta

Update headless developer settings for the website: enable headless mode (so the Content Delivery API and blog publishing work without publishing the hosted template site) 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). Errors: FORBIDDEN, VALIDATION (details.issues).

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/developer-settings" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"headlessEnabled":true,"externalStorefrontUrl":"https://www.sunnybakery.example"}'
POST/sites/{id}/preview-tokens

site.create_preview_token

Beta

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.

Permission
site:manage_settings
Rate limit
30 per hour

Path parameters

  • idstringRequired

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

curl
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}'
GET/sites/{id}/webhooks

webhooks.list

BetaRead-only

List the website's webhook endpoints, recent deliveries, plan quota (Pro: 3, Business: 10) and subscribable event keys. Errors: FORBIDDEN.

Permission
site:integrations
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/webhooks" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/webhooks

webhooks.create

Beta

Create a webhook endpoint (public https URL). Returns the HMAC-SHA256 signingSecret (whsec_…) once — store it in your receiver's environment to verify X-Kanorio-Signature. Pass events (from webhooks.list subscribableEvents) to subscribe to specific events, or omit/null to subscribe to all events. Errors: FORBIDDEN, PLAN_REQUIRED, WEBHOOK_LIMIT_REACHED, DUPLICATE_WEBHOOK, VALIDATION.

Permission
site:integrations
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/webhooks" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.sunnybakery.example/api/revalidate","label":"Vercel ISR Revalidate","events":["collections.item.published","collections.items.bulkChanged"]}'
PUT/sites/{id}/webhooks/{webhookId}/events

webhooks.update_events

Beta

Replace the subscribed event list and optional filter (collectionKeys / formIds) of a webhook endpoint. Pass null for events to subscribe to all events (including future events). Filters require the Business plan. Errors: FORBIDDEN, WEBHOOK_NOT_FOUND, PLAN_REQUIRED, VALIDATION.

Permission
site:integrations
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • webhookIdstringRequired

    Webhook destination id. Get it from webhooks.list.

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/webhooks/{webhookId}/events" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"events":["collections.item.published"],"filter":{"collectionKeys":["blog"]}}'
DELETE/sites/{id}/webhooks/{webhookId}

webhooks.delete

Beta

Delete a webhook endpoint. Errors: FORBIDDEN, WEBHOOK_NOT_FOUND.

Permission
site:integrations
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • webhookIdstringRequired

    Webhook destination id. Get it from webhooks.list.

curl
curl -X DELETE "https://app.kanorio.com/api/v1/sites/{id}/webhooks/{webhookId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/webhooks/{webhookId}/test

webhooks.test

Beta

Send a one-off team.test ping to a webhook endpoint and return the HTTP status and latency (does not count toward auto-disable failures). Errors: FORBIDDEN, WEBHOOK_NOT_FOUND.

Permission
site:integrations
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • webhookIdstringRequired

    Webhook destination id. Get it from webhooks.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/webhooks/{webhookId}/test" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Forms

GET/sites/{id}/forms

forms.list

BetaRead-only

List this website's online forms with status, availability and submission counts. Errors: FORBIDDEN.

Permission
marketing:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/forms" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/forms/{formId}

forms.get

BetaRead-only

Get one form including its field definitions and settings (read-only; forms are edited in the console). Errors: FORBIDDEN, FORM_NOT_FOUND.

Permission
marketing:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • formIdstringRequired

    Form id. Get it from forms.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/forms/{formId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/forms/{formId}/submissions

submissions.list

BetaRead-only

List submissions of one form, newest first, with answers keyed by field id. Filter by status, full-text query and recent days. Attachments are returned as metadata only; downloading files and exporting CSV are not available to agents. Errors: FORBIDDEN, FORM_NOT_FOUND.

Permission
marketing:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • formIdstringRequired

    Form id. Get it from forms.list.

Query parameters

  • pageinteger

    1-based page number.

  • pageSizeinteger

    10-100; defaults to 50.

  • statusstring
  • qstring

    Full-text search in answers.

  • daysinteger

    Only submissions from the last N days in the website's timezone; one of 1, 7, 30, 90.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/forms/{formId}/submissions?page=1&pageSize=20" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/forms/{formId}/submissions/{submissionId}

submissions.get

BetaRead-only

Get one submission of a form with its answers and attachment metadata. Errors: FORBIDDEN, SUBMISSION_NOT_FOUND.

Permission
marketing:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • formIdstringRequired

    Form id. Get it from forms.list.

  • submissionIdstringRequired
curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/forms/{formId}/submissions/{submissionId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Contacts

GET/sites/{id}/contacts

contacts.list

BetaRead-only

List contacts (50 per page) with email marketing status, tags and purchase summary. Filter by status, source, buyers only, or search on email/name/phone; sort by RECENT, SPENT or LAST_ORDER. Errors: FORBIDDEN.

Permission
customers:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • pageinteger

    1-based page number.

  • searchstring
  • statusstring
  • sourcestring
  • buyerstring

    BUYERS = has at least one paid order.

  • sortstring
curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/contacts?page=1" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/contacts

contacts.add

Beta

Add one contact by email (source MANUAL). Only set marketingConsent=true when the person has explicitly agreed to receive marketing email; otherwise the contact is created without consent. Errors: FORBIDDEN, DUPLICATE (email already exists), SUPPRESSED (address is blocked), INVALID_EMAIL, INVALID_PHONE.

Permission
customers:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/contacts" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"customer@example.com","name":"Alex Chen","tags":["cake-order"],"marketingConsent":false}'
GET/sites/{id}/contacts/{contactId}

contacts.get

BetaRead-only

Get one contact with consent state, note, custom profile attributes, purchase summary with recent orders, and the last 50 timeline events/messages. Errors: FORBIDDEN, CONTACT_NOT_FOUND.

Permission
customers:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • contactIdstringRequired

    Contact id. Get it from contacts.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/contacts/{contactId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/contacts/{contactId}

contacts.update_tags_note

Beta

Replace a contact's tags and/or owner note (provide at least one; the other keeps its current value). Tags are trimmed, de-duplicated, max 20 × 50 chars. Errors: FORBIDDEN, VALIDATION, CONTACT_NOT_FOUND.

Permission
customers:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • contactIdstringRequired

    Contact id. Get it from contacts.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/contacts/{contactId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tags":["VIP","cake-order"],"note":"Prefers less sugar."}'
GET/sites/{id}/segments

segments.list

BetaRead-only

List contact segments with their rules and live audience counts. Use a segment id as campaigns.* audience. Errors: FORBIDDEN.

Permission
customers:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/segments" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Products

GET/sites/{id}/products

products.list

BetaRead-only

List products (ACTIVE first, then ARCHIVED) with images, variants and paid order counts; storeCurrency is the ISO 4217 code all amounts use. Errors: FORBIDDEN.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • statusstring
  • limitinteger

    Page size (1-100). Defaults vary per capability.

  • offsetinteger

    Number of items to skip.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/products" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/products

products.create

Beta

Create an ACTIVE product. Products have no draft state: it appears in the store immediately once it has at least one published variant (add variants/prices in the console). Images are media-library assets: pass their assetId (upload with media.upload or media.import_stock first). Slug is generated from the title when omitted. Errors: FORBIDDEN, VALIDATION (details.reason), PRODUCT_LIMIT_REACHED, SITE_SUSPENDED, ASSET_NOT_FOUND (details.assetIds: not in this site's media library, in the trash, or its file is missing), ASSET_NOT_ALLOWED (not an image, or a product file).

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/products" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Strawberry Birthday Cake","productType":"PHYSICAL","description":"Seasonal strawberries and fresh whipped cream. Pre-order at least one day ahead.","images":[{"assetId":"asset_example01","alt":"Strawberry birthday cake"}]}'
GET/sites/{id}/products/{productId}

products.get

BetaRead-only

Get one product with images, options and variants. Errors: FORBIDDEN, PRODUCT_NOT_FOUND.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • productIdstringRequired

    Product id. Get it from products.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/products/{productId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/products/{productId}

products.update

Beta

Partially update a product (only provided fields change; images replace the whole list). Changes go live on the public store immediately. Errors: FORBIDDEN, PRODUCT_NOT_FOUND, VALIDATION (details.reason), SITE_SUSPENDED, ASSET_NOT_FOUND (details.assetIds: not in this site's media library, in the trash, or its file is missing), ASSET_NOT_ALLOWED (not an image, or a product file).

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • productIdstringRequired

    Product id. Get it from products.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/products/{productId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"purchaseNotes":"Please leave your preferred pickup time in the order note."}'
POST/sites/{id}/products/{productId}/archive

products.archive

Beta

Archive a product: it disappears from the store but keeps its orders and can be restored in the console. Deleting products is not available to agents. Errors: FORBIDDEN, PRODUCT_NOT_FOUND, SITE_SUSPENDED.

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • productIdstringRequired

    Product id. Get it from products.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/products/{productId}/archive" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
GET/sites/{id}/product-categories

product_categories.list

BetaRead-only

List product categories (id, slug, name, sortOrder). Category assignment is managed in the console. Errors: FORBIDDEN.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/product-categories" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Deliverables & access

GET/sites/{id}/deliverables

deliverables.list

BetaRead-only

List deliverables (courses, e-books, audio series, template packs) with unit counts and linked products. A deliverable is what a buyer receives; products only sell it. Errors: FORBIDDEN.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • pageinteger

    1-based page number.

  • statusstring
  • searchstring
curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverables" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/deliverables

deliverables.create

Beta

Create a deliverable. Add chapters and units with deliverable_units.replace_tree, then link it to a product with products.update({ deliverables }) or grant it directly with entitlements.grant. Use structure DOWNLOAD for a plain file bundle (e-book, template pack) and COURSE when learners should see chapters and progress. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, KEY_TAKEN, VALIDATION (details.issues), ASSET_NOT_FOUND (details.assetIds).

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/deliverables" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key":"sourdough-fundamentals","title":"Sourdough Fundamentals","description":"Six-week video course with downloadable recipe cards.","coverAssetId":"asset_example01","structure":"COURSE"}'
GET/sites/{id}/deliverables/{deliverableId}

deliverables.get

BetaRead-only

Get one deliverable with settings and the full chapter/unit tree (no signed file URLs). Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • deliverableIdstringRequired

    Deliverable id or key. Get it from deliverables.list.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PATCH/sites/{id}/deliverables/{deliverableId}

deliverables.update

Beta

Partially update a deliverable (title, description, cover, structure, settings such as completionRule/certificate/comments). Units are managed with deliverable_units.*. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, KEY_TAKEN, VALIDATION (details.issues), ASSET_NOT_FOUND (details.assetIds).

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • deliverableIdstringRequired

    Deliverable id or key. Get it from deliverables.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"completionRule":{"type":"percent","percent":80},"certificate":{"enabled":true,"title":"Certificate of Completion","signatureName":"Head Baker"}}}'
POST/sites/{id}/deliverables/{deliverableId}/archive

deliverables.archive

Beta

Archive a deliverable: it can no longer be granted or sold, existing learners keep access until you revoke their entitlements. Deliverables are never deleted. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND.

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • deliverableIdstringRequired

    Deliverable id or key. Get it from deliverables.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}/archive" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
PUT/sites/{id}/deliverables/{deliverableId}/units

deliverable_units.replace_tree

Beta

Replace the whole chapter/unit tree of a deliverable (PUT semantics): units with an id are updated, units without an id are created, units missing from the payload are deleted along with learner progress on them. Two levels only: top-level nodes may have children; children cannot. For a flat download bundle just pass units without children. Upload files with media.upload first and reference them by primaryAssetId / attachmentAssetIds. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, UNIT_TREE_INVALID (details.reason), ASSET_NOT_FOUND, VALIDATION.

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • deliverableIdstringRequired

    Deliverable id or key. Get it from deliverables.list.

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}/units" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"units":[{"kind":"chapter","title":"Week 1 · Getting started","children":[{"kind":"video","title":"Welcome and course overview","primaryAssetId":"asset_example01","isPreview":true},{"kind":"text","title":"Set up your workspace","body":{"markdown":"## Install\n\n1. Download the starter kit…"},"releaseAfterDays":2}]}]}'
POST/sites/{id}/deliverables/{deliverableId}/units/reorder

deliverable_units.reorder

Beta

Reorder sibling units: pass every id of one level (top level when parentId is null) in the new order. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, UNIT_TREE_INVALID.

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • deliverableIdstringRequired

    Deliverable id or key. Get it from deliverables.list.

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}/units/reorder" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"parentId":"unit_chapter01","orderedIds":["unit_text01","unit_video01"]}'
PATCH/sites/{id}/deliverable-units/{unitId}

deliverable_units.update

Beta

Update one unit in place (title, file, preview flag, unlock schedule, tier scope) without touching the tree. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, ASSET_NOT_FOUND, VALIDATION.

Permission
commerce:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • unitIdstringRequired

    Unit id. Get it from deliverables.get.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/deliverable-units/{unitId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Welcome!","isPreview":true}'
GET/sites/{id}/entitlements

entitlements.list

BetaRead-only

List entitlements (who can access which deliverable) filtered by contact or deliverable; includes revoked and expired rows. Errors: FORBIDDEN.

Permission
customers:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • pageinteger

    1-based page number.

  • contactIdstring
  • deliverableIdstring

    Deliverable id or key. Get it from deliverables.list.

  • activeOnlyboolean

    Only rows that are not revoked and not expired.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/entitlements" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/entitlements

entitlements.grant

Beta

Grant a contact access to a deliverable without a purchase (gift, corporate seat, lead magnet). Idempotent: an existing active manual grant for the same deliverable is extended instead of duplicated. Errors: FORBIDDEN, CONTACT_NOT_FOUND, DELIVERABLE_NOT_FOUND (archived deliverables cannot be granted).

Permission
customers:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/entitlements" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"contactId":"contact_example01","deliverableId":"dlv_example01"}'
GET/sites/{id}/entitlements/check

entitlements.check

BetaRead-only

Check whether a contact can access a deliverable or a single unit right now (entitlement, free preview, release schedule, expiry). Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND.

Permission
customers:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • contactIdstringRequired
  • deliverableIdstring

    Deliverable id or key. Get it from deliverables.list.

  • unitIdstring

    Unit id. Get it from deliverables.get.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/entitlements/check?contactId=contact_example01&deliverableId=dlv_example01" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/entitlements/{entitlementId}/revoke

entitlements.revoke

Beta

Revoke one entitlement (learner loses access immediately; the row is kept for audit). Errors: FORBIDDEN, ENTITLEMENT_NOT_FOUND.

Permission
customers:manage
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • entitlementIdstringRequired
curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/entitlements/{entitlementId}/revoke" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Corporate seat reassigned"}'
GET/sites/{id}/deliverables/{deliverableId}/progress

progress.list

BetaRead-only

List learner progress for one deliverable (percent, completed, per-unit status). One row per contact that has an active entitlement. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND.

Permission
customers:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • deliverableIdstringRequired

    Deliverable id or key. Get it from deliverables.list.

Query parameters

  • pageinteger

    1-based page number.

  • contactIdstring
curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}/progress" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Orders

GET/sites/{id}/orders

orders.list

BetaRead-only

List orders newest first with line items and shipment summary (amounts in minor units of order.currency). Refunds, payment settings and invoices are not available to agents. Errors: FORBIDDEN.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • statusstring
  • limitinteger

    Page size (1-100). Defaults vary per capability.

  • offsetinteger

    Number of items to skip.

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/orders" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/orders/{orderId}

orders.get

BetaRead-only

Get one order with line items and shipments. Errors: FORBIDDEN, ORDER_NOT_FOUND.

Permission
store:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • orderIdstringRequired
curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/orders/{orderId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Campaigns

GET/sites/{id}/campaigns

campaigns.list

BetaRead-only

List email campaigns (newest updated first) with status, audience segment and delivery stats for sent ones. Errors: FORBIDDEN, PLAN_REQUIRED.

Permission
marketing:send
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/campaigns" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
POST/sites/{id}/campaigns

campaigns.create_draft

Beta

Create an email campaign as a DRAFT. Agents cannot send or schedule campaigns; the owner reviews and sends from the console. content is optional; when provided it must match the email design-template content shape (subject, previewText, blocks). Audience defaults to the all-subscribers segment. Reference images as asset:<assetId> (media.upload, media.import_stock). Errors: FORBIDDEN, PLAN_REQUIRED, VALIDATION, INVALID_CONTENT (details.detail lists issues), CHANNEL_UNAVAILABLE, ASSET_NOT_FOUND, ASSET_NOT_ALLOWED.

Permission
marketing:send
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/campaigns" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"October seasonal bake","channel":"EMAIL"}'
PATCH/sites/{id}/campaigns/{campaignId}

campaigns.update_draft

Beta

Update a DRAFT campaign's name, content and/or audience segment. Only drafts can be changed by agents (NOT_EDITABLE otherwise); scheduling and sending stay in the console. Errors: FORBIDDEN, PLAN_REQUIRED, CAMPAIGN_NOT_FOUND, NOT_EDITABLE, INVALID_CONTENT, INVALID_AUDIENCE, SEGMENT_NOT_FOUND, ASSET_NOT_FOUND, ASSET_NOT_ALLOWED.

Permission
marketing:send
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

  • campaignIdstringRequired

    Campaign id. Get it from campaigns.list.

curl
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/campaigns/{campaignId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"audience":{"kind":"segment","segmentId":"seg_example01"}}'

Analytics

GET/sites/{id}/analytics/overview

analytics.overview

BetaRead-only

Get website traffic overview for the selected period (default 30 days): pageviews, unique visitors, sessions, button clicks, form submissions, external link clicks, top traffic sources, UTM campaigns, and countries. Errors: FORBIDDEN.

Permission
analytics:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • daysinteger

    Number of days to aggregate (1 to 90, defaults to 30).

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/overview?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/analytics/pages

analytics.pages

BetaRead-only

Get per-page traffic, entry sessions, bounce rate, engagement duration, button clicks, and form submissions (excluding transient checkout pages). Errors: FORBIDDEN.

Permission
analytics:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • daysinteger

    Number of days to aggregate (1 to 90, defaults to 30).

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/pages?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
GET/sites/{id}/analytics/bots

analytics.bots

BetaRead-only

Get AI crawler and search bot activity summary (GPTBot, ClaudeBot, PerplexityBot, Googlebot, etc.) for the selected period. Errors: FORBIDDEN.

Permission
analytics:view
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

Query parameters

  • daysinteger

    Number of days to aggregate (1 to 90, defaults to 30).

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/analytics/bots?days=30" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Publishing

POST/sites/{id}/publish

site.publish

Beta

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, FREE_VISIBLE_MODULE_LIMIT, FREE_VISIBLE_PAGE_LIMIT, CONTENT_VALIDATION_FAILED (details.issues lists pages/fields to fix), MODERATION_LOCKED, EMPTY_CONTENT, HOSTNAME_CONFLICT.

Permission
site:lifecycle
Rate limit
30 per hour

Path parameters

  • idstringRequired

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

curl
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"}'
POST/sites/{id}/unpublish

site.unpublish

Beta

Take the website offline (content and settings are kept; publish again to restore). Only the website owner may do this. Errors: OWNER_REQUIRED, WEBSITE_NOT_FOUND, FORBIDDEN.

Permission
site:lifecycle
Rate limit
30 per hour

Path parameters

  • idstringRequired

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

curl
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/unpublish" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Domains

GET/sites/{id}/domains

domains.list

BetaRead-only

List the custom domains bound to a website (primary first) with their verification status. Errors: FORBIDDEN, NOT_FOUND.

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/domains" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
PUT/sites/{id}/domains

domains.bind

Beta

Set the complete list of custom domains for a website: the first hostname becomes the primary (canonical) domain, the rest become aliases that 301-redirect to it. Hostnames missing from the list are unbound; pass an empty list to remove all custom domains. After binding, the owner must point DNS (CNAME) at Kanorio; call domains.status to see the exact record and verification progress. Requires a paid plan. Errors: FORBIDDEN, PRO_REQUIRED, INVALID_FORMAT, BLOCKED, HIGH_RISK_TLD, LIMIT_REACHED (details.max), TAKEN (used by another site), READD_COOLDOWN (details.retryAfterSeconds).

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/domains" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostnames":["www.sunnybakery.example","sunnybakery.example"]}'
GET/sites/{id}/domains/status

domains.status

BetaRead-only

Re-check DNS/TLS for every PENDING custom domain right now and return the current list. Domains that are still PENDING include a `diagnoses[hostname]` entry explaining what record is missing (expected vs. observed). Also returns the primary binding. Poll every 15-60 seconds after domains.bind until status is ACTIVE; DNS propagation can take minutes to hours. Errors: FORBIDDEN.

Permission
site:manage_settings
Rate limit
120 per minute

Path parameters

  • idstringRequired

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

curl
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/domains/status" \
  -H "Authorization: Bearer $KANORIO_TOKEN"

Error codes

Failures always return an error object with code, message and details. Branch on code; the message wording may change. The table lists every error code and its HTTP status.
Example: the page changed after you read it (409)
{
  "error": {
    "code": "CONFLICT",
    "message": "The resource changed since you read it; re-read it and retry with the new revision marker (updatedAt / contentUpdatedAt).",
    "details": {
      "updatedAt": "2026-10-06T08:05:12.000Z"
    }
  }
}
CodeHTTP
EMPTY_SLUG400
INVALID_BODY400
INVALID_EDITED_PATH400
INVALID_FORMAT400
INVALID_ORDER400
INVALID_SUBDOMAIN400
UNSUPPORTED_EDITED_PATH400
VALIDATION400
UNAUTHENTICATED401
UNAUTHORIZED401
CUSTOM_DOMAIN_CHANGE_FORBIDDEN403
CUSTOM_DOMAIN_LIMIT_REACHED403
FORBIDDEN403
FREE_VISIBLE_MODULE_LIMIT403
FREE_VISIBLE_PAGE_LIMIT403
LIMIT_REACHED403
MODERATION_LOCKED403
OWNER_REQUIRED403
PLAN_CANNOT_PUBLISH403
PRO_REQUIRED403
EMPTY_SLUG
400
INVALID_BODY
400
INVALID_EDITED_PATH
400
INVALID_FORMAT
400
INVALID_ORDER
400
INVALID_SUBDOMAIN
400
UNSUPPORTED_EDITED_PATH
400
VALIDATION
400
UNAUTHENTICATED
401
UNAUTHORIZED
401
CUSTOM_DOMAIN_CHANGE_FORBIDDEN
403
CUSTOM_DOMAIN_LIMIT_REACHED
403
FORBIDDEN
403
FREE_VISIBLE_MODULE_LIMIT
403
FREE_VISIBLE_PAGE_LIMIT
403
LIMIT_REACHED
403
MODERATION_LOCKED
403
OWNER_REQUIRED
403
PLAN_CANNOT_PUBLISH
403
PRO_REQUIRED
403
1-20 / 75
1 / 4