The Kanorio Management REST API allows your backend programs to read and write to your websites directly via HTTPS: create and configure websites, read and write page sections and blog posts, manage collections and digital products, upload media library images, query orders and contacts, as well as publish websites and manage custom domains.
Its underlying capabilities are identical to Connecting an AI Assistant (MCP); the difference lies in how they are invoked:
Base URL: https://app.kanorio.com/api/v1
Complete Endpoints, Parameters, and Error Codes (SSOT): All endpoint paths, query parameters, Request/Response examples, rate limits, and error code tables are generated in real-time from the code contract on the public Management API Reference page (machine-readable spec:
https://app.kanorio.com/api/v1/openapi.json). This guide focuses on authentication, concurrency safety, and deprecation policies.
The REST API only accepts starting with knr_pat_. Please include the Authorization header in every request:
Authorization: Bearer knr_pat_your_key
/api/mcp and cannot be used to call the REST API.Security Warning: A
knr_pat_key is equivalent to your personal management identity. Always store it in server-side environment variables or a secret management service. Never hardcode keys into browser-side frontend code, mobile apps, or public Git repositories.
:id)All site-level endpoint paths start with /sites/:id/.... You can copy the websiteId by going to "Site Settings › General" and scrolling to the bottom, or call GET /api/v1/sites to list all sites accessible by your key.
POST /sites/:id/publish to update the live site): Page sections (PUT .../sections), headers and footers (PUT .../globals/header|footer), site-wide buttons and social links, and site appearance settings. Blog post additions and modifications also only update the working copy; you must call POST .../posts/:postId/publish to make them public.items.* default to publish: true; pass publish: false to save as draft), store products, and digital products.ifUnmodifiedSince)To prevent your scripts from overwriting changes made by team members (or another AI assistant) using the dashboard editor, we use Optimistic Concurrency Control when writing page sections and site-wide navigation:
PUT /sites/:id/pages/:pageId/sections): You must first read the page to get the updatedAt timestamp and include ifUnmodifiedSince: "<updatedAt>" in your request.GET /sites/:id/globals to get the contentUpdatedAt timestamp and include ifUnmodifiedSince: "<contentUpdatedAt>" in your request.409 CONFLICT (with the latest timestamp). Please re-read, merge, and resubmit.Set your key as an environment variable:
export KANORIO_TOKEN="knr_pat_your_key"
curl -s https://app.kanorio.com/api/v1/sites \ -H "Authorization: Bearer $KANORIO_TOKEN"
id and updatedAtcurl -s https://app.kanorio.com/api/v1/sites/ws_example123/pages \ -H "Authorization: Bearer $KANORIO_TOKEN"
ifUnmodifiedSincecurl -s -X PUT https://app.kanorio.com/api/v1/sites/ws_example123/pages/pg_example456/sections \ -H "Authorization: Bearer $KANORIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ifUnmodifiedSince": "2026-10-15T08:00:00.000Z", "sections": [ { "id": "sec_hero_1", "type": "page-main-visual", "title": "Artisan Bakery", "description": "Handcrafted sourdough bread and French pastries, baked fresh every morning." } ] }'
curl -s -X POST https://app.kanorio.com/api/v1/sites/ws_example123/publish \ -H "Authorization: Bearer $KANORIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{}'
429 RATE_LIMITED with a Retry-After header.Deprecation, Sunset, and Link headers, and we will proactively notify owners of keys that have called the endpoint within the last 30 days.Yes. You can use @kanorio/sdk (a typed client automatically generated from the Agent, Delivery, and Customer OpenAPI specs), or run npx @kanorio/cli types --site https://{your-site} to automatically generate full TypeScript types for your collections.
Please visit the Management API Reference — Error codes section. When writing your code, always handle logic based on error.code rather than matching human-readable error.message strings.