Kanorio

API 參考

Media

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

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

List media

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

List the website's media library (the same files the user sees in the dashboard): public and protected images, videos, audio, PDFs and documents, newest first, plus storage usage (usedBytes vs limitBytes). Filter with assetKind, visibility, unusedOnly and search (file name). Files in the trash, older versions kept after media.replace, and broken records where the original file is missing (unless includeMissing=true) are not listed; files in the trash still count toward storage. Errors: FORBIDDEN, INVALID_CURSOR.

Parameters

  • idstring · pathrequired

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

  • assetKind"image" | "video" | "audio" | "pdf" | "document" | "product" · query

    Only this kind of file; all kinds by default. Deprecated value product: same as visibility=protected (removed in the next major version).

  • visibility"public" | "protected" · query

    Only public or only protected files; both by default.

  • tagsstring · query

    Comma-separated tags; only files that carry every listed tag (case-insensitive) are returned.

  • unusedOnlyboolean | "true" | "false" · query

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

  • includeMissingboolean | "true" | "false" · query

    Include broken asset records where the original file no longer exists on storage (defaults to false).

  • searchstring · query

    Part of the file name.

  • sort"newest" | "oldest" | "largest" · query

    newest (default), oldest or largest.

  • limitinteger · query

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

  • cursorstring · query

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

Response 200

  • dataobject[]required
  • data[].assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • data[].kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • data[].visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • data[].tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • data[].processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • data[].processing.status"pending" | "processing" | "failed"required
  • data[].processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • data[].processing.errorstring | nullrequired

    Error code when status is failed.

  • data[].processing.updatedAtstring | nullrequired
  • data[].streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • data[].status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • data[].missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • data[].versionintegerrequired

    Increases each time media.replace uploads a new version.

  • data[].urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • data[].thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • data[].fileNamestring | nullrequired
  • data[].mimeTypestringrequired
  • data[].bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • data[].billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • data[].widthinteger | nullrequired
  • data[].heightinteger | nullrequired
  • data[].durationSecondsnumber | nullrequired
  • data[].pageCountinteger | nullrequired
  • data[].sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • data[].inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • data[].focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • data[].focus.xnumberrequired
  • data[].focus.ynumberrequired
  • data[].edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • data[].altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • data[].watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • data[].createdAtstringrequired
  • hasMorebooleanrequired

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

  • nextCursorstring | nullrequired

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

  • usageobjectrequired
  • usage.usedBytesnumberrequired

    Bytes counted against the storage limit (user-uploaded originals; platform derivatives excluded).

  • usage.limitBytesnumberrequired

    Plan storage limit in bytes (including any purchased storage add-on levels, e.g. 10,737,418,240 for 10 GB on PRO).

  • usage.remainingBytesnumberrequired

    Remaining available storage in bytes (Math.max(0, limitBytes - usedBytes)).

  • usage.totalBytesnumberrequired

    Total bytes stored on this website including platform derivatives (favicons, auto OG images).

  • usage.quotaBytesnumberrequired

    Plan storage limit in bytes (alias of limitBytes; compare usedBytes <= quotaBytes to check whether storage is within quota).

  • usage.fileCountintegerrequired

    Active file count on this website.

Errors

  • FORBIDDEN 403
  • INVALID_CURSOR 400

MCP tool media_list

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media?assetKind=image&unusedOnly=false" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "data": [
    {
      "assetId": "asset_example01",
      "kind": "image",
      "visibility": "public",
      "tags": [],
      "processing": null,
      "status": "active",
      "missing": false,
      "version": 1,
      "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
      "thumbnailUrl": null,
      "fileName": "sunny-bakery-hero.jpg",
      "mimeType": "image/jpeg",
      "bytes": 482133,
      "streaming": "none",
      "billableBytes": 482133,
      "width": 2400,
      "height": 1600,
      "durationSeconds": null,
      "pageCount": null,
      "source": "IMAGE_CONTENT",
      "inUse": true,
      "focus": null,
      "edgeColor": null,
      "altText": null,
      "watermarkExempt": false,
      "createdAt": "2026-10-01T02:30:00.000Z"
    }
  ],
  "usage": {
    "usedBytes": 482133,
    "limitBytes": 5368709120,
    "remainingBytes": 5368226987,
    "totalBytes": 482133,
    "quotaBytes": 5368709120,
    "fileCount": 1
  },
  "hasMore": false,
  "nextCursor": null
}

Upload a media file

POST/sites/{id}/media
betamedia:managedefault (120/minute per token)

Upload one image or file 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 (it also takes videos and documents), media.list_page_images for a page the user owns, or media.search_stock when they have no photos. Images: put asset.url in sections (pages.replace_sections), blog posts and product images. Videos, audio, PDFs, documents, e-books and ZIP files: pass fileName; use visibility "protected" for deliverable files (course lessons, e-books, downloads) and reference them by assetId in deliverable_units.*. Files over 40 MB (base64) or 100 MB (url): use media.request_upload. 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, INVALID_FILE_DATA, UNSUPPORTED_IMAGE_TYPE, UNSUPPORTED_FILE_TYPE (extension not accepted, or the file header does not match it), FILE_TOO_LARGE, FETCH_FAILED, UPLOAD_FAILED, SERVICE_UNAVAILABLE, STORAGE_LIMIT_EXCEEDED (plan storage is full; every upload counts toward it).

Parameters

  • idstring · pathrequired

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

Request body

  • base64string

    File bytes as base64 (plain or data: URL), max 40 MB decoded. Images: JPEG, PNG, WebP, GIF, SVG or HEIC, stored as-is (only downscaled when over Cloudflare limits). Other files need fileName.

  • urlstring

    Public https URL to copy from: images up to 40 MB, other files up to 100 MB (use media.request_upload for larger files). Private networks and redirects to them are rejected.

  • fileNamestring

    File name with extension, e.g. lesson-01.mp4 or workbook.pdf. Required for videos, audio, PDFs, documents, e-books and ZIP files (the extension decides the format; the file header must match). Omit for images.

  • visibility"public" | "protected"

    public (default): files for pages, posts and collections; anyone with the link can open them. protected: files for deliverables (paid courses, e-books, downloads) stored privately and only served to buyers through signed short-lived links. Protected images (JPG, PNG, WebP; pass fileName) are stored as private files too: no public variants, not offered in page or post pickers, not usable as deliverable covers.

  • usagestring

    Hint for bookkeeping (images only): content (default), blog, product, favicon.

Response 201

  • assetobjectrequired
  • asset.assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • asset.kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • asset.visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • asset.tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • asset.processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • asset.processing.status"pending" | "processing" | "failed"required
  • asset.processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • asset.processing.errorstring | nullrequired

    Error code when status is failed.

  • asset.processing.updatedAtstring | nullrequired
  • asset.streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • asset.status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • asset.missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • asset.versionintegerrequired

    Increases each time media.replace uploads a new version.

  • asset.urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • asset.thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • asset.fileNamestring | nullrequired
  • asset.mimeTypestringrequired
  • asset.bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • asset.billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • asset.widthinteger | nullrequired
  • asset.heightinteger | nullrequired
  • asset.durationSecondsnumber | nullrequired
  • asset.pageCountinteger | nullrequired
  • asset.sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • asset.inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • asset.focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • asset.focus.xnumberrequired
  • asset.focus.ynumberrequired
  • asset.edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • asset.altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • asset.watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • asset.createdAtstringrequired

Errors

  • FORBIDDEN 403
  • VALIDATION 400
  • INVALID_IMAGE_DATA 400
  • INVALID_FILE_DATA 400
  • UNSUPPORTED_IMAGE_TYPE 422
  • UNSUPPORTED_FILE_TYPE 422 · extension not accepted, or the file header does not match it
  • FILE_TOO_LARGE 413
  • FETCH_FAILED 422
  • UPLOAD_FAILED 502
  • SERVICE_UNAVAILABLE 503
  • STORAGE_LIMIT_EXCEEDED 403 · plan storage is full; every upload counts toward it

MCP tool media_upload

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"}'
Response
{
  "asset": {
    "assetId": "asset_example01",
    "kind": "image",
    "visibility": "public",
    "tags": [],
    "processing": null,
    "status": "active",
    "missing": false,
    "version": 1,
    "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
    "thumbnailUrl": null,
    "fileName": "croissant.jpg",
    "mimeType": "image/jpeg",
    "bytes": 482133,
    "streaming": "none",
    "billableBytes": 482133,
    "width": 2400,
    "height": 1600,
    "durationSeconds": null,
    "pageCount": null,
    "source": "IMAGE_CONTENT",
    "inUse": false,
    "focus": null,
    "edgeColor": null,
    "altText": null,
    "watermarkExempt": false,
    "createdAt": "2026-10-06T08:00:00.000Z"
  }
}

Request upload

POST/sites/{id}/media/uploads
betamedia:managedefault (120/minute per token)

Get a one-time upload URL for a large video, audio, PDF, document, e-book or ZIP file (up to 5 GB) that is too big for media.upload. Send the file with an HTTP PUT to upload.url, with exactly the returned upload.headers and the file bytes as the body (curl -X PUT -H ... --data-binary @file), within an hour; then call media.confirm_upload with fileKey. Unconfirmed uploads are discarded after 24 hours. Use visibility "protected" for deliverable files (no public URL; only served to entitled buyers). Images go through media.upload. Errors: FORBIDDEN, VALIDATION, UNSUPPORTED_FILE_TYPE, INVALID_FILE_DATA (fileSize is 0), FILE_TOO_LARGE, STORAGE_LIMIT_EXCEEDED, SERVICE_UNAVAILABLE.

Parameters

  • idstring · pathrequired

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

Request body

  • fileNamestringrequired

    File name with extension; the extension decides the format.

  • fileSizeintegerrequired

    Exact size in bytes; the upload must match it.

  • visibility"public" | "protected"

    public (default): files for pages, posts and collections; anyone with the link can open them. protected: files for deliverables (paid courses, e-books, downloads) stored privately and only served to buyers through signed short-lived links. Protected images (JPG, PNG, WebP; pass fileName) are stored as private files too: no public variants, not offered in page or post pickers, not usable as deliverable covers.

Response 201

  • fileKeystringrequired

    Pass to media.confirm_upload.

  • uploadobjectrequired
  • upload.method"PUT"required
  • upload.urlstringrequired
  • upload.headersobjectrequired

    Send every header exactly as given; a missing or changed header is rejected.

  • upload.expiresAtstringrequired

Errors

  • FORBIDDEN 403
  • VALIDATION 400
  • UNSUPPORTED_FILE_TYPE 422
  • INVALID_FILE_DATA 400 · fileSize is 0
  • FILE_TOO_LARGE 413
  • STORAGE_LIMIT_EXCEEDED 403
  • SERVICE_UNAVAILABLE 503

MCP tool media_request_upload

curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/uploads" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"lesson-01.mp4","fileSize":734003200,"visibility":"protected"}'
Response
{
  "fileKey": "ws_example01/deliverables/0123456789abcdef0123456789abcd",
  "upload": {
    "method": "PUT",
    "url": "https://kanorio-products.example.r2.cloudflarestorage.com/ws_example01/deliverables/0123456789abcdef0123456789abcd?X-Amz-Signature=example",
    "headers": {
      "Content-Type": "video/mp4",
      "x-amz-meta-filename": "lesson-01.mp4"
    },
    "expiresAt": "2026-10-06T08:00:00.000Z"
  }
}

Confirm upload

POST/sites/{id}/media/uploads/confirm
betamedia:managedefault (120/minute per token)

Finish a media.request_upload: the file is checked (size, format and file header must match the request) and added to the media library. Calling it again for the same fileKey returns the same asset. Protected files are referenced by assetId in deliverable_units.*; public files by asset.url in sections, posts and collections. Errors: FORBIDDEN, UPLOAD_NOT_FOUND (not uploaded yet, expired, or the fileKey is not from this website), UNSUPPORTED_FILE_TYPE, INVALID_FILE_DATA, FILE_TOO_LARGE, STORAGE_LIMIT_EXCEEDED, SERVICE_UNAVAILABLE, UPLOAD_FAILED.

Parameters

  • idstring · pathrequired

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

Request body

  • fileKeystringrequired

    fileKey from media.request_upload.

  • visibility"public" | "protected"

    The same visibility passed to media.request_upload.

Response 201

  • assetobjectrequired
  • asset.assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • asset.kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • asset.visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • asset.tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • asset.processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • asset.processing.status"pending" | "processing" | "failed"required
  • asset.processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • asset.processing.errorstring | nullrequired

    Error code when status is failed.

  • asset.processing.updatedAtstring | nullrequired
  • asset.streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • asset.status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • asset.missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • asset.versionintegerrequired

    Increases each time media.replace uploads a new version.

  • asset.urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • asset.thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • asset.fileNamestring | nullrequired
  • asset.mimeTypestringrequired
  • asset.bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • asset.billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • asset.widthinteger | nullrequired
  • asset.heightinteger | nullrequired
  • asset.durationSecondsnumber | nullrequired
  • asset.pageCountinteger | nullrequired
  • asset.sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • asset.inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • asset.focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • asset.focus.xnumberrequired
  • asset.focus.ynumberrequired
  • asset.edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • asset.altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • asset.watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • asset.createdAtstringrequired

Errors

  • FORBIDDEN 403
  • UPLOAD_NOT_FOUND 404 · not uploaded yet, expired, or the fileKey is not from this website
  • UNSUPPORTED_FILE_TYPE 422
  • INVALID_FILE_DATA 400
  • FILE_TOO_LARGE 413
  • STORAGE_LIMIT_EXCEEDED 403
  • SERVICE_UNAVAILABLE 503
  • UPLOAD_FAILED 502

MCP tool media_confirm_upload

curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/media/uploads/confirm" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileKey":"ws_example01/deliverables/0123456789abcdef0123456789abcd","visibility":"protected"}'
Response
{
  "asset": {
    "assetId": "asset_example01",
    "kind": "video",
    "visibility": "protected",
    "tags": [],
    "processing": null,
    "status": "active",
    "missing": false,
    "version": 1,
    "url": null,
    "thumbnailUrl": null,
    "fileName": "lesson-01.mp4",
    "mimeType": "video/mp4",
    "bytes": 734003200,
    "streaming": "none",
    "billableBytes": 734003200,
    "width": null,
    "height": null,
    "durationSeconds": null,
    "pageCount": null,
    "source": "PRODUCT",
    "inUse": false,
    "focus": null,
    "edgeColor": null,
    "altText": null,
    "watermarkExempt": false,
    "createdAt": "2026-10-06T08:00:00.000Z"
  }
}

Search stock

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

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

Parameters

  • idstring · pathrequired

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

  • querystring · queryrequired

    English search terms work best.

  • orientation"landscape" | "portrait" | "square" · query
  • provider"all" | "pexels" | "pixabay" · query

    Defaults to all (results interleaved).

  • limitinteger · query

    Maximum number of items to return, 1–12. Defaults to 9.

  • cursorstring · query

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

Response 200

  • dataobject[]required
  • data[].idstringrequired
  • data[].providerstringrequired

    pexels or pixabay.

  • data[].previewURLstringrequired

    Small preview; do not put this in sections.

  • data[].displayURLstringrequired
  • data[].downloadURLstringrequired

    Pass to media.import_stock together with id and provider.

  • data[].widthintegerrequired
  • data[].heightintegerrequired
  • data[].photographerNamestringrequired
  • data[].photoPageURLstringrequired
  • data[].altstring | nullrequired
  • hasMorebooleanrequired

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

  • nextCursorstring | nullrequired

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

Errors

  • FORBIDDEN 403
  • SEARCH_BUSY 429 · retry after details.retryAfter seconds
  • SEARCH_FAILED 502
  • UNKNOWN_PROVIDER 422
  • INVALID_CURSOR 400

MCP tool media_search_stock

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media/stock?query=sourdough+bread+bakery+counter&orientation=landscape&limit=3" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "data": [
    {
      "id": "1234567",
      "provider": "pexels",
      "previewURL": "https://images.pexels.com/photos/1234567/pexels-photo-1234567.jpeg?w=280",
      "displayURL": "https://images.pexels.com/photos/1234567/pexels-photo-1234567.jpeg?w=1280",
      "downloadURL": "https://images.pexels.com/photos/1234567/pexels-photo-1234567.jpeg",
      "width": 4000,
      "height": 2667,
      "photographerName": "Example Photographer",
      "photoPageURL": "https://www.pexels.com/photo/1234567/",
      "alt": "Sourdough loaves on a wooden counter"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Import stock

POST/sites/{id}/media/stock
betamedia:managedefault (120/minute per token)

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.

Parameters

  • idstring · pathrequired

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

Request body

  • provider"pexels" | "pixabay"required
  • idstringrequired

    Stock photo id from the search result.

  • downloadURLstringrequired

    downloadURL from the search result.

  • usagestring

    Bookkeeping hint: content (default), blog, product.

Response 201

  • assetobjectrequired
  • asset.assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • asset.kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • asset.visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • asset.tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • asset.processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • asset.processing.status"pending" | "processing" | "failed"required
  • asset.processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • asset.processing.errorstring | nullrequired

    Error code when status is failed.

  • asset.processing.updatedAtstring | nullrequired
  • asset.streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • asset.status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • asset.missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • asset.versionintegerrequired

    Increases each time media.replace uploads a new version.

  • asset.urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • asset.thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • asset.fileNamestring | nullrequired
  • asset.mimeTypestringrequired
  • asset.bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • asset.billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • asset.widthinteger | nullrequired
  • asset.heightinteger | nullrequired
  • asset.durationSecondsnumber | nullrequired
  • asset.pageCountinteger | nullrequired
  • asset.sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • asset.inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • asset.focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • asset.focus.xnumberrequired
  • asset.focus.ynumberrequired
  • asset.edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • asset.altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • asset.watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • asset.createdAtstringrequired

Errors

  • FORBIDDEN 403
  • UNKNOWN_PROVIDER 422
  • INVALID_STOCK_URL 400
  • STOCK_BUSY 429
  • INVALID_IMAGE_DATA 400
  • UPLOAD_FAILED 502
  • STORAGE_LIMIT_EXCEEDED 403

MCP tool media_import_stock

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"}'
Response
{
  "asset": {
    "assetId": "asset_example01",
    "kind": "image",
    "visibility": "public",
    "tags": [],
    "processing": null,
    "status": "active",
    "missing": false,
    "version": 1,
    "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
    "thumbnailUrl": null,
    "fileName": "pexels-1234567.jpeg",
    "mimeType": "image/jpeg",
    "bytes": 482133,
    "streaming": "none",
    "billableBytes": 482133,
    "width": 2400,
    "height": 1600,
    "durationSeconds": null,
    "pageCount": null,
    "source": "IMAGE_CONTENT",
    "inUse": false,
    "focus": null,
    "edgeColor": null,
    "altText": null,
    "watermarkExempt": false,
    "createdAt": "2026-10-06T08:00:00.000Z"
  }
}

List page images

GET/sites/{id}/media/page-images
betamedia:viewdefault (120/minute per token)

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

Parameters

  • idstring · pathrequired

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

  • urlstring · queryrequired
  • maxinteger · query

    Defaults to 12.

Response 200

  • dataobject[]required
  • data[].urlstringrequired
  • data[].role"cover" | "content" | "logo"required
  • data[].ownAssetbooleanrequired

    false when hosted on a website-builder CDN (likely a platform asset, not the brand's).

  • hasMorebooleanrequired

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

  • nextCursorstring | nullrequired

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

  • pageUrlstringrequired
  • finalUrlstringrequired

    After redirects.

  • titlestring | nullrequired
  • siteNamestring | nullrequired

Errors

  • FORBIDDEN 403
  • INVALID_URL 400 · private or non-http address
  • FETCH_FAILED 422 · details.status is the upstream status

MCP tool media_list_page_images

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"
Response
{
  "pageUrl": "https://old-site.example.com/",
  "finalUrl": "https://old-site.example.com/",
  "title": "Sunny Bakery",
  "siteName": "Sunny Bakery",
  "data": [
    {
      "url": "https://old-site.example.com/images/og.jpg",
      "role": "cover",
      "ownAsset": true
    },
    {
      "url": "https://old-site.example.com/images/logo.png",
      "role": "logo",
      "ownAsset": true
    },
    {
      "url": "https://old-site.example.com/images/croissant.jpg",
      "role": "content",
      "ownAsset": true
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Retrieve a media file

GET/sites/{id}/media/{assetId}
betamedia:viewdefault (120/minute per token)

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.

Parameters

  • idstring · pathrequired

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

  • assetIdstring · pathrequired

    Asset id from media.list.

Response 200

  • assetobjectrequired
  • asset.assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • asset.kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • asset.visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • asset.tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • asset.processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • asset.processing.status"pending" | "processing" | "failed"required
  • asset.processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • asset.processing.errorstring | nullrequired

    Error code when status is failed.

  • asset.processing.updatedAtstring | nullrequired
  • asset.streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • asset.status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • asset.missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • asset.versionintegerrequired

    Increases each time media.replace uploads a new version.

  • asset.urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • asset.thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • asset.fileNamestring | nullrequired
  • asset.mimeTypestringrequired
  • asset.bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • asset.billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • asset.widthinteger | nullrequired
  • asset.heightinteger | nullrequired
  • asset.durationSecondsnumber | nullrequired
  • asset.pageCountinteger | nullrequired
  • asset.sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • asset.inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • asset.focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • asset.focus.xnumberrequired
  • asset.focus.ynumberrequired
  • asset.edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • asset.altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • asset.watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • asset.createdAtstringrequired
  • usagesobject[]required

    Empty when no content uses the file.

  • usages[].kindstringrequired

    pageSection, pageShare, siteContent, brand, favicon, watermark, post, blogTaxonomy, product, productContent, deliverable, productCategory, collectionItem, floatingWidgets, messageTemplate or campaign.

  • usages[].labelstringrequired

    Where it is used, in words you can repeat to the user (page › section, post title, product name…).

  • renditionsobject[] | nullrequired

    Playback sources for public files: original, poster when there is one, and for transcoded videos hls (adaptive-streaming master manifest, application/vnd.apple.mpegurl; play this one, fall back to original) plus thumbnails (progress-bar storyboard VTT). Check streaming: partial means only the lowest quality is in the manifest yet. null for protected files; buyers get their sources through the Customer API.

Errors

  • FORBIDDEN 403
  • ASSET_NOT_FOUND 404

MCP tool media_get

curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
Response
{
  "asset": {
    "assetId": "asset_example01",
    "kind": "image",
    "visibility": "public",
    "tags": [],
    "processing": null,
    "status": "active",
    "missing": false,
    "version": 1,
    "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
    "thumbnailUrl": null,
    "fileName": "sunny-bakery-hero.jpg",
    "mimeType": "image/jpeg",
    "bytes": 482133,
    "streaming": "none",
    "billableBytes": 482133,
    "width": 2400,
    "height": 1600,
    "durationSeconds": null,
    "pageCount": null,
    "source": "IMAGE_CONTENT",
    "inUse": true,
    "focus": null,
    "edgeColor": null,
    "altText": null,
    "watermarkExempt": false,
    "createdAt": "2026-10-01T02:30:00.000Z"
  },
  "usages": [
    {
      "kind": "pageSection",
      "label": "Home › Our story"
    },
    {
      "kind": "brand",
      "label": "Brand logo"
    }
  ],
  "renditions": [
    {
      "kind": "original",
      "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
      "mimeType": "image/jpeg",
      "width": 2400,
      "height": 1600,
      "bytes": 482133
    }
  ]
}

Update a media file

PATCH/sites/{id}/media/{assetId}
betamedia:managedefault (120/minute per token)

Set a file's tags and an image's defaults, kept across new versions. tags (any file): replaces the whole list; up to 20 tags of 1-40 characters, case-insensitive duplicates dropped; filter media.list with tags. Images only: focus (0-1 point on the original that image fields without their own framing crop around; null resets to center), altText (used where the image is shown without an alt of its own; null clears it) and watermarkExempt (show this image without the site watermark; needs the site:manage_settings scope and permission). The Delivery API and the editor use the new focus right away; published pages switch after the next site.publish. Errors: FORBIDDEN, ASSET_NOT_FOUND (also for files in the trash and older versions), IMAGES_ONLY (focus or altText on a non-image), TAGS_INVALID, VISIBILITY_BLOCKED (details.reason: in_use | deliverable | trash | missing | processing | unsupported_kind | older_version; details.locations for in_use), VALIDATION.

Parameters

  • idstring · pathrequired

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

  • assetIdstring · pathrequired

    Asset id from media.list.

Request body

  • focusobject | null

    Focal point as 0-1 coordinates on the original image (x from the left, y from the top); null resets to center.

  • focus.xnumberrequired
  • focus.ynumberrequired
  • altTextstring | null

    Default alt text in the site's content language; null clears it.

  • tagsstring[]

    Replaces the file's tags (any file). Up to 20 tags of 1-40 characters; no commas, line breaks or leading #.

  • visibility"public" | "protected"

    Move a video, audio, PDF or document between public and protected. Images cannot be switched (public images live in the image CDN, protected images in private storage): upload the image again with the visibility you need (VISIBILITY_BLOCKED, reason unsupported_kind). The file is moved between storage buckets in the background: the asset shows processing until done and media.asset.ready fires. public → protected is refused while any content uses the file (VISIBILITY_BLOCKED, reason in_use, with locations); protected → public is refused while a deliverable unit uses it (reason deliverable). Old public URLs may stay cached at the CDN edge for a while after the move.

  • watermarkExemptboolean

    true shows this image without the site watermark.

  • streamingtrue

    Videos only: queue the adaptive-streaming (HLS) version now instead of waiting for the hourly check that transcodes videos placed in pages, posts, collections or deliverable units. The lowest quality becomes playable first and the rest follow; media.asset.ready fires when done. Streaming versions count toward the site's storage quota and cannot be removed separately from the video (ADR 0008). VALIDATION when the file is not a video.

Response 200

  • assetobjectrequired
  • asset.assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • asset.kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • asset.visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • asset.tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • asset.processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • asset.processing.status"pending" | "processing" | "failed"required
  • asset.processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • asset.processing.errorstring | nullrequired

    Error code when status is failed.

  • asset.processing.updatedAtstring | nullrequired
  • asset.streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • asset.status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • asset.missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • asset.versionintegerrequired

    Increases each time media.replace uploads a new version.

  • asset.urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • asset.thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • asset.fileNamestring | nullrequired
  • asset.mimeTypestringrequired
  • asset.bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • asset.billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • asset.widthinteger | nullrequired
  • asset.heightinteger | nullrequired
  • asset.durationSecondsnumber | nullrequired
  • asset.pageCountinteger | nullrequired
  • asset.sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • asset.inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • asset.focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • asset.focus.xnumberrequired
  • asset.focus.ynumberrequired
  • asset.edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • asset.altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • asset.watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • asset.createdAtstringrequired

Errors

  • FORBIDDEN 403
  • ASSET_NOT_FOUND 404 · also for files in the trash and older versions
  • IMAGES_ONLY 422 · focus or altText on a non-image
  • TAGS_INVALID 422
  • VISIBILITY_BLOCKED 422 · details.reason: in_use | deliverable | trash | missing | processing | unsupported_kind | older_version; details.locations for in_use
  • VALIDATION 400

MCP tool media_update

curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/media/{assetId}" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"focus":{"x":0.32,"y":0.4},"altText":"Fresh croissants on the bakery counter"}'
Response
{
  "asset": {
    "assetId": "asset_example01",
    "kind": "image",
    "visibility": "public",
    "tags": [],
    "processing": null,
    "status": "active",
    "missing": false,
    "version": 1,
    "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
    "thumbnailUrl": null,
    "fileName": "sunny-bakery-hero.jpg",
    "mimeType": "image/jpeg",
    "bytes": 482133,
    "streaming": "none",
    "billableBytes": 482133,
    "width": 2400,
    "height": 1600,
    "durationSeconds": null,
    "pageCount": null,
    "source": "IMAGE_CONTENT",
    "inUse": true,
    "focus": {
      "x": 0.32,
      "y": 0.4
    },
    "edgeColor": null,
    "altText": "Fresh croissants on the bakery counter",
    "watermarkExempt": false,
    "createdAt": "2026-10-01T02:30:00.000Z"
  }
}

Media file purge

DELETE/sites/{id}/media/{assetId}
betamedia:managedefault (120/minute per token)

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.

Parameters

  • idstring · pathrequired

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

  • assetIdstring · pathrequired

    Asset id from media.list.

  • confirmboolean | "true" | "false" · query

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

Response 200

  • assetIdstringrequired
  • deletedtruerequired

Errors

  • FORBIDDEN 403
  • NOT_FOUND 404
  • NOT_IN_TRASH 409
  • PRODUCT_FILE_PROTECTED 409
  • ASSET_IN_USE 422
  • EXPORT_HOLD 409 · details.until: an export keeps the file until then
  • DELETE_FAILED 502

MCP tool media_purge

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

Replace a media file

POST/sites/{id}/media/{assetId}/replace
betamedia:managedefault (120/minute per token)

Upload a new version of an image or file (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, blog posts and deliverables 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. Videos, audio, PDFs and documents need fileName and can only become the same kind (video to video, PDF to PDF). Replacing a protected file changes what buyers of the deliverable get right away. Files over 40 MB (base64) or 100 MB (url) 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), KIND_MISMATCH, REPLACE_NOT_AVAILABLE, VALIDATION, INVALID_IMAGE_DATA, INVALID_FILE_DATA, UNSUPPORTED_IMAGE_TYPE, UNSUPPORTED_FILE_TYPE, FILE_TOO_LARGE, FETCH_FAILED, STORAGE_LIMIT_EXCEEDED, SERVICE_UNAVAILABLE, UPLOAD_FAILED.

Parameters

  • idstring · pathrequired

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

  • assetIdstring · pathrequired

    Asset id from media.list.

Request body

  • base64string

    File bytes as base64 (plain or data: URL), max 40 MB decoded. Images: JPEG, PNG, WebP, GIF, SVG or HEIC, stored as-is (only downscaled when over Cloudflare limits). Other files need fileName.

  • urlstring

    Public https URL to copy from: images up to 40 MB, other files up to 100 MB (use media.request_upload for larger files). Private networks and redirects to them are rejected.

  • fileNamestring

    Name of the new version with extension (required when replacing a video, audio, PDF, document or protected file; must be the same kind as the current file).

Response 200

  • assetobjectrequired

    The file after the replacement (version increased).

  • asset.assetIdstringrequired

    Stable id of the file. Reference files by this id; it stays the same when media.replace uploads a new version.

  • asset.kind"image" | "video" | "audio" | "pdf" | "document"required

    What the file is (image, video, audio, pdf, document). Whether it is protected is a separate property: visibility.

  • asset.visibility"public" | "protected"required

    public: has a public URL and can be placed in pages, posts and collections. protected: no public URL; only served to entitled buyers through signed short-lived links (reference it by assetId in deliverable_units.*).

  • asset.tagsstring[]required

    Owner-defined tags (up to 20 per file, 1-40 characters each); filter media.list with tags.

  • asset.processingobject | nullrequired

    Background processing of the file itself (thumbnails, visibility moves, transcoding): null when the file is ready to use. Subscribe to the media.asset.ready and media.asset.failed webhooks instead of polling.

  • asset.processing.status"pending" | "processing" | "failed"required
  • asset.processing.kindstring | nullrequired

    Background job kind (for example DOCUMENT_THUMBNAIL).

  • asset.processing.errorstring | nullrequired

    Error code when status is failed.

  • asset.processing.updatedAtstring | nullrequired
  • asset.streaming"none" | "processing" | "partial" | "ready"required

    Adaptive-streaming (HLS) version of a video: none (not placed anywhere that plays it, or not a video), processing (queued or transcoding), partial (lowest quality playable, higher qualities still transcoding), ready (all qualities done). Videos are transcoded when first placed in a page, post, collection or deliverable unit, or with media.update { streaming: true }; the streaming version counts toward storage (billableBytes).

  • asset.status"active" | "trash"required

    trash = in the trash: not shown on the site, restorable with media.restore.

  • asset.missingbooleanrequired

    true when the original no longer exists; places that use it show nothing (excluded from media.list by default unless includeMissing=true).

  • asset.versionintegerrequired

    Increases each time media.replace uploads a new version.

  • asset.urlstring | nullrequired

    Display URL for showing the file to the user. It changes with each version, so never store it; null for protected files.

  • asset.thumbnailUrlstring | nullrequired

    Thumbnail or poster URL for public files; null for protected files (their thumbnails are only served to signed-in members).

  • asset.fileNamestring | nullrequired
  • asset.mimeTypestringrequired
  • asset.bytesinteger | nullrequired

    null while the size of an older file is still being calculated.

  • asset.billableBytesintegerrequired

    Bytes counted toward the site's storage quota (images have a 256 KB minimum; active derived files such as watermarked versions are included).

  • asset.widthinteger | nullrequired
  • asset.heightinteger | nullrequired
  • asset.durationSecondsnumber | nullrequired
  • asset.pageCountinteger | nullrequired
  • asset.sourcestringrequired

    Where it was uploaded (page module, blog, product, media library…).

  • asset.inUsebooleanrequired

    Used by site content, products, deliverables, emails or a published snapshot.

  • asset.focusobject | nullrequired

    Default focal point of an image (0-1 on the original): image fields without their own framing crop around it. null when unset or centered.

  • asset.focus.xnumberrequired
  • asset.focus.ynumberrequired
  • asset.edgeColorstring | nullrequired

    Solid border color (#rrggbb) when all four edges of the image share a uniform opaque color (for example a white-background product photo). null for transparent or multi-colored images.

  • asset.altTextstring | nullrequired

    Default alt text, used when the place that shows the image has no alt of its own.

  • asset.watermarkExemptbooleanrequired

    true when the owner excluded this image from the site watermark.

  • asset.createdAtstringrequired
  • usagesobject[]required

    Places that now show the new version.

  • usages[].kindstringrequired

    pageSection, pageShare, siteContent, brand, favicon, watermark, post, blogTaxonomy, product, productContent, deliverable, productCategory, collectionItem, floatingWidgets, messageTemplate or campaign.

  • usages[].labelstringrequired

    Where it is used, in words you can repeat to the user (page › section, post title, product name…).

  • awaitingPublishbooleanrequired

    Published pages still show the older version until the site is published again.

  • aspectRatioChangedbooleanrequired

Errors

  • FORBIDDEN 403
  • ASSET_NOT_FOUND 404 · also for files in the trash
  • KIND_MISMATCH 422
  • REPLACE_NOT_AVAILABLE 422
  • VALIDATION 400
  • INVALID_IMAGE_DATA 400
  • INVALID_FILE_DATA 400
  • UNSUPPORTED_IMAGE_TYPE 422
  • UNSUPPORTED_FILE_TYPE 422
  • FILE_TOO_LARGE 413
  • FETCH_FAILED 422
  • STORAGE_LIMIT_EXCEEDED 403
  • SERVICE_UNAVAILABLE 503
  • UPLOAD_FAILED 502

MCP tool media_replace

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"}'
Response
{
  "asset": {
    "assetId": "asset_example01",
    "kind": "image",
    "visibility": "public",
    "tags": [],
    "processing": null,
    "status": "active",
    "missing": false,
    "version": 2,
    "url": "https://media.kanorio.com/example/sunny-bakery-hero.jpg",
    "thumbnailUrl": null,
    "fileName": "sunny-bakery-hero.jpg",
    "mimeType": "image/jpeg",
    "bytes": 482133,
    "streaming": "none",
    "billableBytes": 482133,
    "width": 2400,
    "height": 1600,
    "durationSeconds": null,
    "pageCount": null,
    "source": "IMAGE_CONTENT",
    "inUse": true,
    "focus": null,
    "edgeColor": null,
    "altText": null,
    "watermarkExempt": false,
    "createdAt": "2026-10-01T02:30:00.000Z"
  },
  "usages": [
    {
      "kind": "pageSection",
      "label": "Home › Our story"
    }
  ],
  "awaitingPublish": true,
  "aspectRatioChanged": false
}

Media file trash

POST/sites/{id}/media/{assetId}/trash
betamedia:managedefault (120/minute per token)

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. Protected files that a deliverable uses cannot be trashed; remove them from the deliverable first (unused protected files can be trashed). Errors: FORBIDDEN, NOT_FOUND, PRODUCT_FILE_PROTECTED.

Parameters

  • idstring · pathrequired

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

  • assetIdstring · pathrequired

    Asset id from media.list.

Response 200

  • assetIdstringrequired
  • usagesobject[]required

    Places on the site that now show nothing; empty when the file was unused.

  • usages[].kindstringrequired

    pageSection, pageShare, siteContent, brand, favicon, watermark, post, blogTaxonomy, product, productContent, deliverable, productCategory, collectionItem, floatingWidgets, messageTemplate or campaign.

  • usages[].labelstringrequired

    Where it is used, in words you can repeat to the user (page › section, post title, product name…).

  • deleteAfterstringrequired

    ISO time after which the file is deleted permanently.

Errors

  • FORBIDDEN 403
  • NOT_FOUND 404
  • PRODUCT_FILE_PROTECTED 409

MCP tool media_trash

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 '{}'
Response
{
  "assetId": "asset_example01",
  "usages": [
    {
      "kind": "pageSection",
      "label": "Home › Our story"
    }
  ],
  "deleteAfter": "2026-11-05T08:00:00.000Z"
}

Restore a media file

POST/sites/{id}/media/{assetId}/restore
betamedia:managedefault (120/minute per token)

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.

Parameters

  • idstring · pathrequired

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

  • assetIdstring · pathrequired

    Asset id from media.list.

Response 200

  • assetIdstringrequired

Errors

  • FORBIDDEN 403
  • NOT_FOUND 404
  • PRODUCT_FILE_PROTECTED 409
  • RESTORE_FAILED 422

MCP tool media_restore

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 '{}'
Response
{
  "assetId": "asset_example01"
}