API 레퍼런스
Deliverables
Management API: GET /sites/{id}/deliverables, POST /sites/{id}/deliverables, GET /sites/{id}/deliverables/{deliverableId}, …
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, INVALID_CURSOR.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
limitinteger · queryMaximum number of items to return, 1–100. Defaults to 20.
cursorstring · queryOpaque cursor from the previous page's nextCursor. Omit for the first page.
status"ALL" | "ACTIVE" | "ARCHIVED" · querysearchstring · query
Response 200
dataobject[]requireddata[].idstringrequireddata[].titlestringrequireddata[].descriptionstring | nullrequireddata[].coverAssetIdstring | nullrequireddata[].structure"COURSE" | "DOWNLOAD"requiredCOURSE has chapters and tracks progress; DOWNLOAD is a flat file list (no progress, new units allow download by default). Access rules are identical.
data[].status"ACTIVE" | "ARCHIVED"requireddata[].grantsstring[]requiredCommercial feature keys (e.g. ["premium"]) granted when a buyer purchases or is granted this deliverable; matched by ContentCollection.accessPolicy and entitlements.check.
data[].authorIdsstring[]requiredAuthors/instructors shown on the sales page and the learner page, in order. Ids come from the site's shared author list (also used by posts; managed in the console under Posts › Settings).
data[].unitCountintegerrequiredLeaf units only; chapters are not counted.
data[].productIdsstring[]requiredProducts that grant this deliverable on purchase (see products.update deliverables).
data[].createdAtstringrequireddata[].updatedAtstringrequiredhasMorebooleanrequiredTrue when another page exists; pass nextCursor to fetch it.
nextCursorstring | nullrequiredCursor for the next page, or null on the last page.
Errors
FORBIDDEN403INVALID_CURSOR400
MCP tool deliverables_list
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverables" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"data": [
{
"id": "dlv_example01",
"title": "Sourdough Fundamentals",
"description": "Six-week video course with downloadable recipe cards.",
"coverAssetId": "asset_example01",
"structure": "COURSE",
"status": "ACTIVE",
"grants": [
"premium"
],
"authorIds": [
"author_headbaker01"
],
"unitCount": 3,
"productIds": [
"prod_example01"
],
"createdAt": "2026-10-01T02:30:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}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. The structure cannot be changed later; to switch, copy the deliverable with deliverables.duplicate. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, VALIDATION (details.issues), ASSET_NOT_FOUND (details.assetIds: not in the media library, in the trash or an older version), ASSET_NOT_ALLOWED (coverAssetId, certificate.signatureAssetId or certificate.backgroundAssetId is not a public image), AUTHOR_NOT_FOUND (details.authorIds: not in the site's author list).
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
titlestringrequireddescriptionstring | nullcoverAssetIdstring | nullstructure"COURSE" | "DOWNLOAD"COURSE (default) for chapters/progress; DOWNLOAD for a flat file list. Fixed after creation.
grantsstring[]Optional feature keys (e.g. ["premium"]) unlocked by this deliverable for collection accessPolicy and entitlements.check.
authorIdsstring[]Authors/instructors (ids from the site's shared author list, replaced as a whole, order kept). Unknown ids fail with AUTHOR_NOT_FOUND.
settingsobjectsettings.completionRuleobjectsettings.completionRule.type"all_units"requiredsettings.certificateobjectWhen enabled, a certificate (serial + issue date) is issued the first time a learner completes the deliverable; learners fetch it with Customer API GET /deliverables/:id/certificate. The PDF is generated on download from the current settings, course title, learner name and site branding (logo, brand colour), so edits apply to every certificate; only the serial and issue date are fixed. Send every certificate field when updating it. Use deliverables.certificate_preview to see it.
settings.certificate.enabledbooleanrequiredsettings.certificate.titlestring | nullrequiredCertificate heading; null = the site-language default ("Certificate of Completion").
settings.certificate.signatureNamestring | nullrequiredSigner's name or organisation; null = no signature block.
settings.certificate.signatureTitlestring | nullrequiredSigner's title printed under the name, e.g. "Lead instructor".
settings.certificate.signatureAssetIdstring | nullrequiredSignature image: a public image asset id (same rule as coverAssetId); printed above the signature line.
settings.certificate.showLogobooleanrequiredPrint the site's brand logo (site settings) at the top; icon logos are skipped.
settings.certificate.notestring | nullrequiredOne line printed under the course title, e.g. "12 hours of instruction".
settings.certificate.backgroundAssetIdstring | nullrequiredBackground image: a public image asset id (same rule as coverAssetId), centred and cropped to fill an A4 landscape page (recommended 3508 × 2480 px). When set, the default border is not drawn; text positions stay fixed, so keep the centre of the design clear.
settings.certificate.backgroundOverlaybooleanrequiredLay a semi-transparent white layer over the background image so text stays readable on busy designs; no effect without a background image.
settings.commentsobjectsettings.comments.enabledbooleanrequiredsettings.comments.threadedbooleanrequiredsettings.comments.requireReviewbooleanrequiredsettings.comments.reactionsbooleanrequiredsettings.requireSequentialbooleanLearners must complete each unit before opening the next (free-preview units are exempt). Locked units come back with access.reason prerequisite.
settings.mediaCompletionPercentintegerShare of a video/audio unit a learner must actually watch for it to count as completed (50-100, step 5, default 90). Measured from the segments played, so skipping to the end does not count; 100 keeps a 2-second tolerance at the end. Custom players that only report positionSeconds are measured by the furthest position. Raising it never un-completes learners.
fieldDataobject | null
Response 201
idstringrequiredtitlestringrequireddescriptionstring | nullrequiredcoverAssetIdstring | nullrequiredstructure"COURSE" | "DOWNLOAD"requiredCOURSE has chapters and tracks progress; DOWNLOAD is a flat file list (no progress, new units allow download by default). Access rules are identical.
status"ACTIVE" | "ARCHIVED"requiredgrantsstring[]requiredCommercial feature keys (e.g. ["premium"]) granted when a buyer purchases or is granted this deliverable; matched by ContentCollection.accessPolicy and entitlements.check.
authorIdsstring[]requiredAuthors/instructors shown on the sales page and the learner page, in order. Ids come from the site's shared author list (also used by posts; managed in the console under Posts › Settings).
unitCountintegerrequiredLeaf units only; chapters are not counted.
productIdsstring[]requiredProducts that grant this deliverable on purchase (see products.update deliverables).
createdAtstringrequiredupdatedAtstringrequiredsettingsobjectrequiredsettings.completionRuleobjectrequiredsettings.completionRule.type"all_units"requiredsettings.certificateobjectrequiredWhen enabled, a certificate (serial + issue date) is issued the first time a learner completes the deliverable; learners fetch it with Customer API GET /deliverables/:id/certificate. The PDF is generated on download from the current settings, course title, learner name and site branding (logo, brand colour), so edits apply to every certificate; only the serial and issue date are fixed. Send every certificate field when updating it. Use deliverables.certificate_preview to see it.
settings.certificate.enabledbooleanrequiredsettings.certificate.titlestring | nullrequiredCertificate heading; null = the site-language default ("Certificate of Completion").
settings.certificate.signatureNamestring | nullrequiredSigner's name or organisation; null = no signature block.
settings.certificate.signatureTitlestring | nullrequiredSigner's title printed under the name, e.g. "Lead instructor".
settings.certificate.signatureAssetIdstring | nullrequiredSignature image: a public image asset id (same rule as coverAssetId); printed above the signature line.
settings.certificate.showLogobooleanrequiredPrint the site's brand logo (site settings) at the top; icon logos are skipped.
settings.certificate.notestring | nullrequiredOne line printed under the course title, e.g. "12 hours of instruction".
settings.certificate.backgroundAssetIdstring | nullrequiredBackground image: a public image asset id (same rule as coverAssetId), centred and cropped to fill an A4 landscape page (recommended 3508 × 2480 px). When set, the default border is not drawn; text positions stay fixed, so keep the centre of the design clear.
settings.certificate.backgroundOverlaybooleanrequiredLay a semi-transparent white layer over the background image so text stays readable on busy designs; no effect without a background image.
settings.commentsobjectrequiredsettings.comments.enabledbooleanrequiredsettings.comments.threadedbooleanrequiredsettings.comments.requireReviewbooleanrequiredsettings.comments.reactionsbooleanrequiredsettings.requireSequentialbooleanrequiredLearners must complete each unit before opening the next (free-preview units are exempt). Locked units come back with access.reason prerequisite.
settings.mediaCompletionPercentintegerrequiredShare of a video/audio unit a learner must actually watch for it to count as completed (50-100, step 5, default 90). Measured from the segments played, so skipping to the end does not count; 100 keeps a 2-second tolerance at the end. Custom players that only report positionSeconds are measured by the furthest position. Raising it never un-completes learners.
fieldDataobject | nullrequiredunitsobject[]requiredFlat list ordered by chapter then unit; use parentId to rebuild the tree.
units[].idstringrequiredunits[].parentIdstring | nullrequiredChapter id for units inside a chapter; null for top-level chapters or flat units.
units[].orderintegerrequiredunits[].kindstringrequiredOpen string. Known values: video, audio, file, text, quiz; "chapter" for grouping rows. Unknown kinds render as a download card.
units[].titlestring | nullrequiredunits[].descriptionstring | nullrequiredunits[].primaryAssetIdstring | nullrequiredMedia-library asset (private products bucket). Upload with media.upload first.
units[].attachmentAssetIdsstring[]requiredunits[].fileUrlstring | nullrequiredOnly for Kanorio template sample media (not a media-library asset). Real files are referenced by primaryAssetId.
units[].embedobject | nullrequiredExternal video embed (youtube/vimeo/loom). Not protected by Kanorio; use for FREE sites that cannot host video.
units[].embed.providerstringrequiredunits[].embed.idstringrequiredunits[].bodyanyrequiredtext units: { markdown }. quiz units: { questions: [{ id, type: "single" | "multiple" | "true_false", prompt, options: [{ id, text, correct: boolean }], explanation? }], settings: { passingPercent (0-100, default 70), maxAttempts (1-99 or null = unlimited), showAnswers: "never" | "after_pass" | "always" } }; validated on write (UNIT_TREE_INVALID quiz_body_invalid). Learners never see correct/explanation; the server grades Customer API POST /units/:id/quiz.
units[].isPreviewbooleanrequiredFree preview: anonymous visitors can open it.
units[].visibility"VISIBLE" | "HIDDEN"requiredunits[].releaseAtstring | nullrequiredAbsolute unlock time.
units[].releaseAfterDaysinteger | nullrequiredUnlock N days after the learner's entitlement started.
units[].allowDownloadbooleanrequiredunits[].countsTowardCompletionbooleanrequiredfalse = bonus unit: not counted in percent or completion, never locked by requireSequential and never a gate for later units.
units[].isPrerequisitebooleanrequiredUnits after this one stay locked until the learner completes it, even when requireSequential is off.
productsobject[]requiredProducts (variants) that grant this deliverable; tierId null = every variant. Access duration is pricingTiers[].accessDays on the product.
products[].productIdstringrequiredproducts[].tierIdstring | nullrequired
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404VALIDATION400 · details.issuesASSET_NOT_FOUND404 · details.assetIds: not in the media library, in the trash or an older versionASSET_NOT_ALLOWED422 · coverAssetId, certificate.signatureAssetId or certificate.backgroundAssetId is not a public imageAUTHOR_NOT_FOUND404 · details.authorIds: not in the site's author list
MCP tool deliverables_create
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/deliverables" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Sourdough Fundamentals","description":"Six-week video course with downloadable recipe cards.","coverAssetId":"asset_example01","structure":"COURSE"}'{
"id": "dlv_example01",
"title": "Sourdough Fundamentals",
"description": "Six-week video course with downloadable recipe cards.",
"coverAssetId": "asset_example01",
"structure": "COURSE",
"status": "ACTIVE",
"grants": [
"premium"
],
"authorIds": [
"author_headbaker01"
],
"unitCount": 0,
"productIds": [],
"createdAt": "2026-10-01T02:30:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z",
"settings": {
"completionRule": {
"type": "all_units"
},
"certificate": {
"enabled": false,
"title": null,
"signatureName": null,
"signatureTitle": null,
"signatureAssetId": null,
"showLogo": true,
"note": null,
"backgroundAssetId": null,
"backgroundOverlay": false
},
"comments": {
"enabled": false,
"threaded": true,
"requireReview": true,
"reactions": false
},
"requireSequential": false,
"mediaCompletionPercent": 90
},
"fieldData": null,
"units": [],
"products": []
}Retrieve a deliverable
/sites/{id}/deliverables/{deliverableId}Get one deliverable with settings and the full chapter/unit tree (no signed file URLs). Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
deliverableIdstring · pathrequiredDeliverable id. Get it from deliverables.list.
Response 200
idstringrequiredtitlestringrequireddescriptionstring | nullrequiredcoverAssetIdstring | nullrequiredstructure"COURSE" | "DOWNLOAD"requiredCOURSE has chapters and tracks progress; DOWNLOAD is a flat file list (no progress, new units allow download by default). Access rules are identical.
status"ACTIVE" | "ARCHIVED"requiredgrantsstring[]requiredCommercial feature keys (e.g. ["premium"]) granted when a buyer purchases or is granted this deliverable; matched by ContentCollection.accessPolicy and entitlements.check.
authorIdsstring[]requiredAuthors/instructors shown on the sales page and the learner page, in order. Ids come from the site's shared author list (also used by posts; managed in the console under Posts › Settings).
unitCountintegerrequiredLeaf units only; chapters are not counted.
productIdsstring[]requiredProducts that grant this deliverable on purchase (see products.update deliverables).
createdAtstringrequiredupdatedAtstringrequiredsettingsobjectrequiredsettings.completionRuleobjectrequiredsettings.completionRule.type"all_units"requiredsettings.certificateobjectrequiredWhen enabled, a certificate (serial + issue date) is issued the first time a learner completes the deliverable; learners fetch it with Customer API GET /deliverables/:id/certificate. The PDF is generated on download from the current settings, course title, learner name and site branding (logo, brand colour), so edits apply to every certificate; only the serial and issue date are fixed. Send every certificate field when updating it. Use deliverables.certificate_preview to see it.
settings.certificate.enabledbooleanrequiredsettings.certificate.titlestring | nullrequiredCertificate heading; null = the site-language default ("Certificate of Completion").
settings.certificate.signatureNamestring | nullrequiredSigner's name or organisation; null = no signature block.
settings.certificate.signatureTitlestring | nullrequiredSigner's title printed under the name, e.g. "Lead instructor".
settings.certificate.signatureAssetIdstring | nullrequiredSignature image: a public image asset id (same rule as coverAssetId); printed above the signature line.
settings.certificate.showLogobooleanrequiredPrint the site's brand logo (site settings) at the top; icon logos are skipped.
settings.certificate.notestring | nullrequiredOne line printed under the course title, e.g. "12 hours of instruction".
settings.certificate.backgroundAssetIdstring | nullrequiredBackground image: a public image asset id (same rule as coverAssetId), centred and cropped to fill an A4 landscape page (recommended 3508 × 2480 px). When set, the default border is not drawn; text positions stay fixed, so keep the centre of the design clear.
settings.certificate.backgroundOverlaybooleanrequiredLay a semi-transparent white layer over the background image so text stays readable on busy designs; no effect without a background image.
settings.commentsobjectrequiredsettings.comments.enabledbooleanrequiredsettings.comments.threadedbooleanrequiredsettings.comments.requireReviewbooleanrequiredsettings.comments.reactionsbooleanrequiredsettings.requireSequentialbooleanrequiredLearners must complete each unit before opening the next (free-preview units are exempt). Locked units come back with access.reason prerequisite.
settings.mediaCompletionPercentintegerrequiredShare of a video/audio unit a learner must actually watch for it to count as completed (50-100, step 5, default 90). Measured from the segments played, so skipping to the end does not count; 100 keeps a 2-second tolerance at the end. Custom players that only report positionSeconds are measured by the furthest position. Raising it never un-completes learners.
fieldDataobject | nullrequiredunitsobject[]requiredFlat list ordered by chapter then unit; use parentId to rebuild the tree.
units[].idstringrequiredunits[].parentIdstring | nullrequiredChapter id for units inside a chapter; null for top-level chapters or flat units.
units[].orderintegerrequiredunits[].kindstringrequiredOpen string. Known values: video, audio, file, text, quiz; "chapter" for grouping rows. Unknown kinds render as a download card.
units[].titlestring | nullrequiredunits[].descriptionstring | nullrequiredunits[].primaryAssetIdstring | nullrequiredMedia-library asset (private products bucket). Upload with media.upload first.
units[].attachmentAssetIdsstring[]requiredunits[].fileUrlstring | nullrequiredOnly for Kanorio template sample media (not a media-library asset). Real files are referenced by primaryAssetId.
units[].embedobject | nullrequiredExternal video embed (youtube/vimeo/loom). Not protected by Kanorio; use for FREE sites that cannot host video.
units[].embed.providerstringrequiredunits[].embed.idstringrequiredunits[].bodyanyrequiredtext units: { markdown }. quiz units: { questions: [{ id, type: "single" | "multiple" | "true_false", prompt, options: [{ id, text, correct: boolean }], explanation? }], settings: { passingPercent (0-100, default 70), maxAttempts (1-99 or null = unlimited), showAnswers: "never" | "after_pass" | "always" } }; validated on write (UNIT_TREE_INVALID quiz_body_invalid). Learners never see correct/explanation; the server grades Customer API POST /units/:id/quiz.
units[].isPreviewbooleanrequiredFree preview: anonymous visitors can open it.
units[].visibility"VISIBLE" | "HIDDEN"requiredunits[].releaseAtstring | nullrequiredAbsolute unlock time.
units[].releaseAfterDaysinteger | nullrequiredUnlock N days after the learner's entitlement started.
units[].allowDownloadbooleanrequiredunits[].countsTowardCompletionbooleanrequiredfalse = bonus unit: not counted in percent or completion, never locked by requireSequential and never a gate for later units.
units[].isPrerequisitebooleanrequiredUnits after this one stay locked until the learner completes it, even when requireSequential is off.
productsobject[]requiredProducts (variants) that grant this deliverable; tierId null = every variant. Access duration is pricingTiers[].accessDays on the product.
products[].productIdstringrequiredproducts[].tierIdstring | nullrequired
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404
MCP tool deliverables_get
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"id": "dlv_example01",
"title": "Sourdough Fundamentals",
"description": "Six-week video course with downloadable recipe cards.",
"coverAssetId": "asset_example01",
"structure": "COURSE",
"status": "ACTIVE",
"grants": [
"premium"
],
"authorIds": [
"author_headbaker01"
],
"unitCount": 3,
"productIds": [
"prod_example01"
],
"createdAt": "2026-10-01T02:30:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z",
"settings": {
"completionRule": {
"type": "all_units"
},
"certificate": {
"enabled": false,
"title": null,
"signatureName": null,
"signatureTitle": null,
"signatureAssetId": null,
"showLogo": true,
"note": null,
"backgroundAssetId": null,
"backgroundOverlay": false
},
"comments": {
"enabled": false,
"threaded": true,
"requireReview": true,
"reactions": false
},
"requireSequential": false,
"mediaCompletionPercent": 90
},
"fieldData": null,
"units": [
{
"id": "unit_chapter01",
"parentId": null,
"order": 0,
"kind": "chapter",
"title": "Week 1 · Getting started",
"description": null,
"primaryAssetId": null,
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": null,
"isPreview": false,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": null,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
},
{
"id": "unit_video01",
"parentId": "unit_chapter01",
"order": 0,
"kind": "video",
"title": "Welcome and course overview",
"description": "What you will build in six weeks.",
"primaryAssetId": "asset_example01",
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": null,
"isPreview": true,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": null,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
},
{
"id": "unit_text01",
"parentId": "unit_chapter01",
"order": 1,
"kind": "text",
"title": "Set up your workspace",
"description": null,
"primaryAssetId": null,
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": {
"markdown": "## Install\n\n1. Download the starter kit…"
},
"isPreview": false,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": 2,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
}
],
"products": [
{
"productId": "prod_example01",
"tierId": null
}
]
}Update a deliverable
/sites/{id}/deliverables/{deliverableId}Partially update a deliverable (title, description, cover, settings such as completionRule/certificate/comments). Units are managed with deliverable_units.*. The structure is fixed after creation: sending a different structure fails with STRUCTURE_LOCKED; copy it with deliverables.duplicate instead. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, VALIDATION (details.issues), ASSET_NOT_FOUND (details.assetIds: not in the media library, in the trash or an older version), ASSET_NOT_ALLOWED (coverAssetId, certificate.signatureAssetId or certificate.backgroundAssetId is not a public image), AUTHOR_NOT_FOUND (details.authorIds: not in the site's author list).
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
deliverableIdstring · pathrequiredDeliverable id. Get it from deliverables.list.
Request body
titlestringdescriptionstring | nullcoverAssetIdstring | nullstructure"COURSE" | "DOWNLOAD"Must equal the current structure (fixed after creation); omit it.
grantsstring[]Optional feature keys (e.g. ["premium"]) unlocked by this deliverable for collection accessPolicy and entitlements.check.
authorIdsstring[]Authors/instructors (ids from the site's shared author list, replaced as a whole, order kept). Unknown ids fail with AUTHOR_NOT_FOUND.
settingsobjectsettings.completionRuleobjectsettings.completionRule.type"all_units"requiredsettings.certificateobjectWhen enabled, a certificate (serial + issue date) is issued the first time a learner completes the deliverable; learners fetch it with Customer API GET /deliverables/:id/certificate. The PDF is generated on download from the current settings, course title, learner name and site branding (logo, brand colour), so edits apply to every certificate; only the serial and issue date are fixed. Send every certificate field when updating it. Use deliverables.certificate_preview to see it.
settings.certificate.enabledbooleanrequiredsettings.certificate.titlestring | nullrequiredCertificate heading; null = the site-language default ("Certificate of Completion").
settings.certificate.signatureNamestring | nullrequiredSigner's name or organisation; null = no signature block.
settings.certificate.signatureTitlestring | nullrequiredSigner's title printed under the name, e.g. "Lead instructor".
settings.certificate.signatureAssetIdstring | nullrequiredSignature image: a public image asset id (same rule as coverAssetId); printed above the signature line.
settings.certificate.showLogobooleanrequiredPrint the site's brand logo (site settings) at the top; icon logos are skipped.
settings.certificate.notestring | nullrequiredOne line printed under the course title, e.g. "12 hours of instruction".
settings.certificate.backgroundAssetIdstring | nullrequiredBackground image: a public image asset id (same rule as coverAssetId), centred and cropped to fill an A4 landscape page (recommended 3508 × 2480 px). When set, the default border is not drawn; text positions stay fixed, so keep the centre of the design clear.
settings.certificate.backgroundOverlaybooleanrequiredLay a semi-transparent white layer over the background image so text stays readable on busy designs; no effect without a background image.
settings.commentsobjectsettings.comments.enabledbooleanrequiredsettings.comments.threadedbooleanrequiredsettings.comments.requireReviewbooleanrequiredsettings.comments.reactionsbooleanrequiredsettings.requireSequentialbooleanLearners must complete each unit before opening the next (free-preview units are exempt). Locked units come back with access.reason prerequisite.
settings.mediaCompletionPercentintegerShare of a video/audio unit a learner must actually watch for it to count as completed (50-100, step 5, default 90). Measured from the segments played, so skipping to the end does not count; 100 keeps a 2-second tolerance at the end. Custom players that only report positionSeconds are measured by the furthest position. Raising it never un-completes learners.
fieldDataobject | null
Response 200
idstringrequiredtitlestringrequireddescriptionstring | nullrequiredcoverAssetIdstring | nullrequiredstructure"COURSE" | "DOWNLOAD"requiredCOURSE has chapters and tracks progress; DOWNLOAD is a flat file list (no progress, new units allow download by default). Access rules are identical.
status"ACTIVE" | "ARCHIVED"requiredgrantsstring[]requiredCommercial feature keys (e.g. ["premium"]) granted when a buyer purchases or is granted this deliverable; matched by ContentCollection.accessPolicy and entitlements.check.
authorIdsstring[]requiredAuthors/instructors shown on the sales page and the learner page, in order. Ids come from the site's shared author list (also used by posts; managed in the console under Posts › Settings).
unitCountintegerrequiredLeaf units only; chapters are not counted.
productIdsstring[]requiredProducts that grant this deliverable on purchase (see products.update deliverables).
createdAtstringrequiredupdatedAtstringrequiredsettingsobjectrequiredsettings.completionRuleobjectrequiredsettings.completionRule.type"all_units"requiredsettings.certificateobjectrequiredWhen enabled, a certificate (serial + issue date) is issued the first time a learner completes the deliverable; learners fetch it with Customer API GET /deliverables/:id/certificate. The PDF is generated on download from the current settings, course title, learner name and site branding (logo, brand colour), so edits apply to every certificate; only the serial and issue date are fixed. Send every certificate field when updating it. Use deliverables.certificate_preview to see it.
settings.certificate.enabledbooleanrequiredsettings.certificate.titlestring | nullrequiredCertificate heading; null = the site-language default ("Certificate of Completion").
settings.certificate.signatureNamestring | nullrequiredSigner's name or organisation; null = no signature block.
settings.certificate.signatureTitlestring | nullrequiredSigner's title printed under the name, e.g. "Lead instructor".
settings.certificate.signatureAssetIdstring | nullrequiredSignature image: a public image asset id (same rule as coverAssetId); printed above the signature line.
settings.certificate.showLogobooleanrequiredPrint the site's brand logo (site settings) at the top; icon logos are skipped.
settings.certificate.notestring | nullrequiredOne line printed under the course title, e.g. "12 hours of instruction".
settings.certificate.backgroundAssetIdstring | nullrequiredBackground image: a public image asset id (same rule as coverAssetId), centred and cropped to fill an A4 landscape page (recommended 3508 × 2480 px). When set, the default border is not drawn; text positions stay fixed, so keep the centre of the design clear.
settings.certificate.backgroundOverlaybooleanrequiredLay a semi-transparent white layer over the background image so text stays readable on busy designs; no effect without a background image.
settings.commentsobjectrequiredsettings.comments.enabledbooleanrequiredsettings.comments.threadedbooleanrequiredsettings.comments.requireReviewbooleanrequiredsettings.comments.reactionsbooleanrequiredsettings.requireSequentialbooleanrequiredLearners must complete each unit before opening the next (free-preview units are exempt). Locked units come back with access.reason prerequisite.
settings.mediaCompletionPercentintegerrequiredShare of a video/audio unit a learner must actually watch for it to count as completed (50-100, step 5, default 90). Measured from the segments played, so skipping to the end does not count; 100 keeps a 2-second tolerance at the end. Custom players that only report positionSeconds are measured by the furthest position. Raising it never un-completes learners.
fieldDataobject | nullrequiredunitsobject[]requiredFlat list ordered by chapter then unit; use parentId to rebuild the tree.
units[].idstringrequiredunits[].parentIdstring | nullrequiredChapter id for units inside a chapter; null for top-level chapters or flat units.
units[].orderintegerrequiredunits[].kindstringrequiredOpen string. Known values: video, audio, file, text, quiz; "chapter" for grouping rows. Unknown kinds render as a download card.
units[].titlestring | nullrequiredunits[].descriptionstring | nullrequiredunits[].primaryAssetIdstring | nullrequiredMedia-library asset (private products bucket). Upload with media.upload first.
units[].attachmentAssetIdsstring[]requiredunits[].fileUrlstring | nullrequiredOnly for Kanorio template sample media (not a media-library asset). Real files are referenced by primaryAssetId.
units[].embedobject | nullrequiredExternal video embed (youtube/vimeo/loom). Not protected by Kanorio; use for FREE sites that cannot host video.
units[].embed.providerstringrequiredunits[].embed.idstringrequiredunits[].bodyanyrequiredtext units: { markdown }. quiz units: { questions: [{ id, type: "single" | "multiple" | "true_false", prompt, options: [{ id, text, correct: boolean }], explanation? }], settings: { passingPercent (0-100, default 70), maxAttempts (1-99 or null = unlimited), showAnswers: "never" | "after_pass" | "always" } }; validated on write (UNIT_TREE_INVALID quiz_body_invalid). Learners never see correct/explanation; the server grades Customer API POST /units/:id/quiz.
units[].isPreviewbooleanrequiredFree preview: anonymous visitors can open it.
units[].visibility"VISIBLE" | "HIDDEN"requiredunits[].releaseAtstring | nullrequiredAbsolute unlock time.
units[].releaseAfterDaysinteger | nullrequiredUnlock N days after the learner's entitlement started.
units[].allowDownloadbooleanrequiredunits[].countsTowardCompletionbooleanrequiredfalse = bonus unit: not counted in percent or completion, never locked by requireSequential and never a gate for later units.
units[].isPrerequisitebooleanrequiredUnits after this one stay locked until the learner completes it, even when requireSequential is off.
productsobject[]requiredProducts (variants) that grant this deliverable; tierId null = every variant. Access duration is pricingTiers[].accessDays on the product.
products[].productIdstringrequiredproducts[].tierIdstring | nullrequired
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404VALIDATION400 · details.issuesASSET_NOT_FOUND404 · details.assetIds: not in the media library, in the trash or an older versionASSET_NOT_ALLOWED422 · coverAssetId, certificate.signatureAssetId or certificate.backgroundAssetId is not a public imageAUTHOR_NOT_FOUND404 · details.authorIds: not in the site's author list
MCP tool deliverables_update
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","signatureTitle":"Lead instructor","signatureAssetId":null,"showLogo":true,"note":"12 hours of instruction","backgroundAssetId":"asset_example01","backgroundOverlay":true}}}'{
"id": "dlv_example01",
"title": "Sourdough Fundamentals",
"description": "Six-week video course with downloadable recipe cards.",
"coverAssetId": "asset_example01",
"structure": "COURSE",
"status": "ACTIVE",
"grants": [
"premium"
],
"authorIds": [
"author_headbaker01"
],
"unitCount": 3,
"productIds": [
"prod_example01"
],
"createdAt": "2026-10-01T02:30:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z",
"settings": {
"completionRule": {
"type": "percent",
"percent": 80
},
"certificate": {
"enabled": true,
"title": "Certificate of Completion",
"signatureName": "Head Baker",
"signatureTitle": "Lead instructor",
"signatureAssetId": null,
"showLogo": true,
"note": "12 hours of instruction",
"backgroundAssetId": "asset_example01",
"backgroundOverlay": true
},
"comments": {
"enabled": false,
"threaded": true,
"requireReview": true,
"reactions": false
},
"requireSequential": false,
"mediaCompletionPercent": 90
},
"fieldData": null,
"units": [
{
"id": "unit_chapter01",
"parentId": null,
"order": 0,
"kind": "chapter",
"title": "Week 1 · Getting started",
"description": null,
"primaryAssetId": null,
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": null,
"isPreview": false,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": null,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
},
{
"id": "unit_video01",
"parentId": "unit_chapter01",
"order": 0,
"kind": "video",
"title": "Welcome and course overview",
"description": "What you will build in six weeks.",
"primaryAssetId": "asset_example01",
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": null,
"isPreview": true,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": null,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
},
{
"id": "unit_text01",
"parentId": "unit_chapter01",
"order": 1,
"kind": "text",
"title": "Set up your workspace",
"description": null,
"primaryAssetId": null,
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": {
"markdown": "## Install\n\n1. Download the starter kit…"
},
"isPreview": false,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": 2,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
}
],
"products": [
{
"productId": "prod_example01",
"tierId": null
}
]
}Duplicate a deliverable
/sites/{id}/deliverables/{deliverableId}/duplicateCopy a deliverable with its settings and the whole chapter/unit tree (files are shared, not copied). Product links, entitlements, learner progress and downloads are not copied: the copy starts with no learners. Pass a different structure to switch between COURSE and DOWNLOAD (the structure is fixed after creation); COURSE to DOWNLOAD drops the chapters and keeps their units in the same order. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, VALIDATION (details.issues).
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
deliverableIdstring · pathrequiredDeliverable to copy
Request body
titlestringrequiredTitle of the copy
structure"COURSE" | "DOWNLOAD"Structure of the copy; defaults to the source's.
Response 201
idstringrequiredtitlestringrequireddescriptionstring | nullrequiredcoverAssetIdstring | nullrequiredstructure"COURSE" | "DOWNLOAD"requiredCOURSE has chapters and tracks progress; DOWNLOAD is a flat file list (no progress, new units allow download by default). Access rules are identical.
status"ACTIVE" | "ARCHIVED"requiredgrantsstring[]requiredCommercial feature keys (e.g. ["premium"]) granted when a buyer purchases or is granted this deliverable; matched by ContentCollection.accessPolicy and entitlements.check.
authorIdsstring[]requiredAuthors/instructors shown on the sales page and the learner page, in order. Ids come from the site's shared author list (also used by posts; managed in the console under Posts › Settings).
unitCountintegerrequiredLeaf units only; chapters are not counted.
productIdsstring[]requiredProducts that grant this deliverable on purchase (see products.update deliverables).
createdAtstringrequiredupdatedAtstringrequiredsettingsobjectrequiredsettings.completionRuleobjectrequiredsettings.completionRule.type"all_units"requiredsettings.certificateobjectrequiredWhen enabled, a certificate (serial + issue date) is issued the first time a learner completes the deliverable; learners fetch it with Customer API GET /deliverables/:id/certificate. The PDF is generated on download from the current settings, course title, learner name and site branding (logo, brand colour), so edits apply to every certificate; only the serial and issue date are fixed. Send every certificate field when updating it. Use deliverables.certificate_preview to see it.
settings.certificate.enabledbooleanrequiredsettings.certificate.titlestring | nullrequiredCertificate heading; null = the site-language default ("Certificate of Completion").
settings.certificate.signatureNamestring | nullrequiredSigner's name or organisation; null = no signature block.
settings.certificate.signatureTitlestring | nullrequiredSigner's title printed under the name, e.g. "Lead instructor".
settings.certificate.signatureAssetIdstring | nullrequiredSignature image: a public image asset id (same rule as coverAssetId); printed above the signature line.
settings.certificate.showLogobooleanrequiredPrint the site's brand logo (site settings) at the top; icon logos are skipped.
settings.certificate.notestring | nullrequiredOne line printed under the course title, e.g. "12 hours of instruction".
settings.certificate.backgroundAssetIdstring | nullrequiredBackground image: a public image asset id (same rule as coverAssetId), centred and cropped to fill an A4 landscape page (recommended 3508 × 2480 px). When set, the default border is not drawn; text positions stay fixed, so keep the centre of the design clear.
settings.certificate.backgroundOverlaybooleanrequiredLay a semi-transparent white layer over the background image so text stays readable on busy designs; no effect without a background image.
settings.commentsobjectrequiredsettings.comments.enabledbooleanrequiredsettings.comments.threadedbooleanrequiredsettings.comments.requireReviewbooleanrequiredsettings.comments.reactionsbooleanrequiredsettings.requireSequentialbooleanrequiredLearners must complete each unit before opening the next (free-preview units are exempt). Locked units come back with access.reason prerequisite.
settings.mediaCompletionPercentintegerrequiredShare of a video/audio unit a learner must actually watch for it to count as completed (50-100, step 5, default 90). Measured from the segments played, so skipping to the end does not count; 100 keeps a 2-second tolerance at the end. Custom players that only report positionSeconds are measured by the furthest position. Raising it never un-completes learners.
fieldDataobject | nullrequiredunitsobject[]requiredFlat list ordered by chapter then unit; use parentId to rebuild the tree.
units[].idstringrequiredunits[].parentIdstring | nullrequiredChapter id for units inside a chapter; null for top-level chapters or flat units.
units[].orderintegerrequiredunits[].kindstringrequiredOpen string. Known values: video, audio, file, text, quiz; "chapter" for grouping rows. Unknown kinds render as a download card.
units[].titlestring | nullrequiredunits[].descriptionstring | nullrequiredunits[].primaryAssetIdstring | nullrequiredMedia-library asset (private products bucket). Upload with media.upload first.
units[].attachmentAssetIdsstring[]requiredunits[].fileUrlstring | nullrequiredOnly for Kanorio template sample media (not a media-library asset). Real files are referenced by primaryAssetId.
units[].embedobject | nullrequiredExternal video embed (youtube/vimeo/loom). Not protected by Kanorio; use for FREE sites that cannot host video.
units[].embed.providerstringrequiredunits[].embed.idstringrequiredunits[].bodyanyrequiredtext units: { markdown }. quiz units: { questions: [{ id, type: "single" | "multiple" | "true_false", prompt, options: [{ id, text, correct: boolean }], explanation? }], settings: { passingPercent (0-100, default 70), maxAttempts (1-99 or null = unlimited), showAnswers: "never" | "after_pass" | "always" } }; validated on write (UNIT_TREE_INVALID quiz_body_invalid). Learners never see correct/explanation; the server grades Customer API POST /units/:id/quiz.
units[].isPreviewbooleanrequiredFree preview: anonymous visitors can open it.
units[].visibility"VISIBLE" | "HIDDEN"requiredunits[].releaseAtstring | nullrequiredAbsolute unlock time.
units[].releaseAfterDaysinteger | nullrequiredUnlock N days after the learner's entitlement started.
units[].allowDownloadbooleanrequiredunits[].countsTowardCompletionbooleanrequiredfalse = bonus unit: not counted in percent or completion, never locked by requireSequential and never a gate for later units.
units[].isPrerequisitebooleanrequiredUnits after this one stay locked until the learner completes it, even when requireSequential is off.
productsobject[]requiredProducts (variants) that grant this deliverable; tierId null = every variant. Access duration is pricingTiers[].accessDays on the product.
products[].productIdstringrequiredproducts[].tierIdstring | nullrequiredduplicatedFromstringrequired
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404VALIDATION400 · details.issues
MCP tool deliverables_duplicate
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}/duplicate" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Sourdough Fundamentals (recipe cards)","structure":"DOWNLOAD"}'{
"id": "dlv_example02",
"title": "Sourdough Fundamentals (recipe cards)",
"description": "Six-week video course with downloadable recipe cards.",
"coverAssetId": "asset_example01",
"structure": "DOWNLOAD",
"status": "ACTIVE",
"grants": [
"premium"
],
"authorIds": [
"author_headbaker01"
],
"unitCount": 2,
"productIds": [],
"createdAt": "2026-10-01T02:30:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z",
"settings": {
"completionRule": {
"type": "all_units"
},
"certificate": {
"enabled": false,
"title": null,
"signatureName": null,
"signatureTitle": null,
"signatureAssetId": null,
"showLogo": true,
"note": null,
"backgroundAssetId": null,
"backgroundOverlay": false
},
"comments": {
"enabled": false,
"threaded": true,
"requireReview": true,
"reactions": false
},
"requireSequential": false,
"mediaCompletionPercent": 90
},
"fieldData": null,
"units": [
{
"id": "unit_video02",
"parentId": null,
"order": 0,
"kind": "video",
"title": "Welcome and course overview",
"description": "What you will build in six weeks.",
"primaryAssetId": "asset_example01",
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": null,
"isPreview": true,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": null,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
},
{
"id": "unit_text02",
"parentId": null,
"order": 1,
"kind": "text",
"title": "Set up your workspace",
"description": null,
"primaryAssetId": null,
"attachmentAssetIds": [],
"fileUrl": null,
"embed": null,
"body": {
"markdown": "## Install\n\n1. Download the starter kit…"
},
"isPreview": false,
"visibility": "VISIBLE",
"releaseAt": null,
"releaseAfterDays": 2,
"allowDownload": false,
"countsTowardCompletion": true,
"isPrerequisite": false
}
],
"products": [],
"duplicatedFrom": "dlv_example01"
}Archive a deliverable
/sites/{id}/deliverables/{deliverableId}/archiveArchive 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.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
deliverableIdstring · pathrequiredDeliverable id. Get it from deliverables.list.
Response 200
idstringrequiredtitlestringrequireddescriptionstring | nullrequiredcoverAssetIdstring | nullrequiredstructure"COURSE" | "DOWNLOAD"requiredCOURSE has chapters and tracks progress; DOWNLOAD is a flat file list (no progress, new units allow download by default). Access rules are identical.
status"ACTIVE" | "ARCHIVED"requiredgrantsstring[]requiredCommercial feature keys (e.g. ["premium"]) granted when a buyer purchases or is granted this deliverable; matched by ContentCollection.accessPolicy and entitlements.check.
authorIdsstring[]requiredAuthors/instructors shown on the sales page and the learner page, in order. Ids come from the site's shared author list (also used by posts; managed in the console under Posts › Settings).
unitCountintegerrequiredLeaf units only; chapters are not counted.
productIdsstring[]requiredProducts that grant this deliverable on purchase (see products.update deliverables).
createdAtstringrequiredupdatedAtstringrequired
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404
MCP tool deliverables_archive
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 '{}'{
"id": "dlv_example01",
"title": "Sourdough Fundamentals",
"description": "Six-week video course with downloadable recipe cards.",
"coverAssetId": "asset_example01",
"structure": "COURSE",
"status": "ARCHIVED",
"grants": [
"premium"
],
"authorIds": [
"author_headbaker01"
],
"unitCount": 3,
"productIds": [
"prod_example01"
],
"createdAt": "2026-10-01T02:30:00.000Z",
"updatedAt": "2026-10-06T08:00:00.000Z"
}Certificate preview
/sites/{id}/deliverables/{deliverableId}/certificate-previewShort-lived URL (10 minutes) of a sample certificate PDF for one course deliverable, rendered exactly like a learner's certificate from the saved settings and site branding, with a "Preview" watermark and no serial. Nothing is issued or stored. Pass learnerName to see how a long or non-Latin name fits. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, NOT_A_COURSE (download packs have no certificates).
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
deliverableIdstring · pathrequiredDeliverable id. Get it from deliverables.list.
learnerNamestring · querySample learner name; omitted = the site-language sample name.
Response 200
urlstringrequiredOpen or download the PDF; no auth needed, expires with expiresAt.
expiresInintegerrequiredSeconds until the URL stops working.
expiresAtstringrequiredISO-8601 timestamp with timezone, e.g. 2026-10-05T12:34:56.789Z.
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404NOT_A_COURSE422 · download packs have no certificates
MCP tool deliverables_certificate_preview
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverables/{deliverableId}/certificate-preview?learnerName=Alex+Chen" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"url": "https://app.kanorio.com/api/certificates/eyJrIjoicCJ9.3f1c…",
"expiresIn": 600,
"expiresAt": "2026-10-06T08:00:00.000Z"
}Learning insights for one course or all courses: current state (learner statuses, stalled count, completion rate, median days to complete, quiz pass rate, certificates, expiring learners) plus period activity (active learners, new starts, completions, hours watched, daily actives). With deliverableId: that course's outline (reached/completed per unit, biggest drop) and progress distribution; without it: one row per course for comparison. Numbers that do not apply are null, never 0 (e.g. quizPassRate when the course has no quiz). No learner names: use progress.list for individual learners. Errors: FORBIDDEN, DELIVERABLE_NOT_FOUND, VALIDATION.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
deliverableIdstring · queryOne course; omit for every course on the site.
daysinteger · queryNumber of calendar days ending today in the website's timezone (1 to 365, defaults to 30). Ignored when from and to are provided.
fromstring · queryStart calendar date (YYYY-MM-DD) in the website's timezone, inclusive. Must be paired with to.
tostring · queryEnd calendar date (YYYY-MM-DD) in the website's timezone, inclusive, up to today. Must be paired with from.
Response 200
rangeobjectrequiredrange.fromstringrequiredFirst calendar date of the period (website timezone).
range.tostringrequiredLast calendar date of the period (website timezone).
range.timezonestringrequiredcurrentobjectrequiredcurrent.learnersintegerrequiredContacts with an active entitlement (counted once per course).
current.statusesobjectrequiredcurrent.statuses.notStartedintegerrequiredcurrent.statuses.inProgressintegerrequiredcurrent.statuses.completedintegerrequiredcurrent.statuses.expiredIncompleteintegerrequiredcurrent.stalledintegerrequiredNot started 14 days after being granted, or no activity for 7 days with progress; learners idle more than 90 days are not counted.
current.expiringIncompleteintegerrequiredAccess ends within 14 days and the course is not completed.
current.completedintegerrequiredcurrent.completionRatenumber | nullrequired0-1; null when there are no learners.
current.medianDaysToCompletenumber | nullrequirednull until someone has completed.
current.quizPassRatenumber | nullrequired0-1; null when there is no quiz or nobody has attempted one.
current.certificatesinteger | nullrequiredCertificates issued; null when no course in scope issues certificates.
periodobjectrequiredperiod.activeintegerrequiredLearners with any progress, playback or download in the period.
period.newStartsintegerrequiredperiod.completesintegerrequiredperiod.watchedSecondsinteger | nullrequiredWall-clock playback in the period; null when there are no media units.
period.dailyActiveobject[]requiredperiod.dailyActive[].datestringrequiredperiod.dailyActive[].activeintegerrequiredoutlineobject | nullrequiredOnly with deliverableId.
outline.unitsobject[]requiredoutline.units[].unitIdstringrequiredoutline.units[].titlestringrequiredoutline.units[].chapterTitlestring | nullrequiredoutline.units[].kindstringrequiredoutline.units[].countsTowardCompletionbooleanrequiredoutline.units[].reachedintegerrequiredLearners who opened the unit (cumulative).
outline.units[].completedintegerrequiredoutline.units[].avgCoveredPercentnumber | nullrequiredMedia units: mean share watched per learner, 0-100; otherwise null.
outline.units[].durationnumber | nullrequiredoutline.biggestDropUnitIdstring | nullrequireddistributionobjectrequireddistribution.histogramany[]requiredLearners by progress band: 0-25, 25-50, 50-75, 75-100 percent.
distribution.typicalobject[]requiredMedian progress N days after being granted.
distribution.typical[].dayintegerrequireddistribution.typical[].medianPercentnumber | nullrequireddistribution.typical[].sampleintegerrequireddistribution.sampleintegerrequiredcoursesobject[] | nullrequiredOne row per course; only without deliverableId.
Errors
FORBIDDEN403DELIVERABLE_NOT_FOUND404VALIDATION400
MCP tool deliverables_insights
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/deliverable-insights?deliverableId=dlv_example01&days=30" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"range": {
"from": "2026-09-07",
"to": "2026-10-06",
"timezone": "Asia/Taipei"
},
"current": {
"learners": 60,
"statuses": {
"notStarted": 13,
"inProgress": 35,
"completed": 10,
"expiredIncomplete": 2
},
"stalled": 23,
"expiringIncomplete": 6,
"completed": 10,
"completionRate": 0.17,
"medianDaysToComplete": 12,
"quizPassRate": 0.8,
"certificates": 10
},
"period": {
"active": 41,
"newStarts": 9,
"completes": 4,
"watchedSeconds": 98400,
"dailyActive": [
{
"date": "2026-10-05",
"active": 7
},
{
"date": "2026-10-06",
"active": 9
}
]
},
"outline": {
"units": [
{
"unitId": "unit_video01",
"title": "Welcome",
"chapterTitle": "Week 1 · Getting started",
"kind": "video",
"countsTowardCompletion": true,
"reached": 47,
"completed": 38,
"avgCoveredPercent": 86,
"duration": 612
},
{
"unitId": "unit_text01",
"title": "Reading list",
"chapterTitle": "Week 1 · Getting started",
"kind": "text",
"countsTowardCompletion": true,
"reached": 31,
"completed": 24,
"avgCoveredPercent": null,
"duration": null
}
],
"biggestDropUnitId": "unit_text01"
},
"distribution": {
"histogram": [
18,
14,
13,
15
],
"typical": [
{
"day": 7,
"medianPercent": 25,
"sample": 52
},
{
"day": 30,
"medianPercent": 60,
"sample": 31
}
],
"sample": 60
},
"courses": null
}