Webly
WeblyDocs

Tools

Every MCP tool, generated from the live tool list.

MCP tools

The 109 tools an agent gets over MCP. Each one calls a single API endpoint in-process, with the same permission checks, and returns that endpoint's response. tools/list shows only the tools the caller's role can use.

Remote: POST http://api.webly.ai/v1/mcp with Authorization: Bearer wb_… or OAuth. Local: npm run mcp with WEBLY_API_KEY set. MCP always needs a key or a signed-in person; person tools are hidden from API-key connections. READ tools change nothing, WRITE tools create or change something, and DELETE tools delete something or take it offline.

Websites

READlist_websites{ limit?, cursor? }
List websites the key can access (newest first, cursor-paginated).GET /v1/websites websites.read
WRITEcreate_website{ name, kind?, slug?, subdomain?, description? }
Create a website.POST /v1/websites websites.create
READget_website{ websiteId }
Get a website, including headVersion, publishedVersion, hasUnpublishedChanges, and urls.GET /v1/websites/{id} websites.read
WRITEupdate_website{ websiteId, name?, slug?, description? }
Rename a website or change its slug/description (does not affect hostnames).PATCH /v1/websites/{id} websites.manage
WRITErename_subdomain{ websiteId, label }
Change the platform subdomain (https://{label}.webly.site).PATCH /v1/websites/{id}/subdomain websites.manage
DELETEdelete_website{ websiteId }
Soft-delete a website and take it offline on every hostname.DELETE /v1/websites/{id} websites.manage
WRITErestore_website{ websiteId }
Restore a soft-deleted website.POST /v1/websites/{id}/restore websites.manage
WRITEstart_website_transfer{ websiteId, toOrganizationId? }
Start moving a website to another organization.POST /v1/websites/{id}/transfer websites.manage
WRITEcancel_website_transfer{ websiteId }
Cancel a pending website transfer.DELETE /v1/websites/{id}/transfer websites.manage
READget_pending_transfer{ websiteId }
The pending outgoing transfer for a website, if any (status, target organization, expiry).GET /v1/websites/{id}/transfer websites.manage
WRITEaccept_website_transfer{ code }
Accept a website transfer into your organization using its code.POST /v1/websites/transfers/accept websites.create
WRITEcreate_claim_code{ none }
Claim a website that was deployed without an account (and files shared from the same computer within their 24 hours), without reading its secret.POST /v1/websites/claim-codes websites.read person
WRITEclaim_anonymous_site{ token }
Claim a website that was deployed without an account into the signed-in person's personal workspace.POST /v1/websites/claims websites.read person

Source and versions

WRITEdeploy_files{ websiteId, files?, deletes?, replace?, expectedHeadVersion?, message? }
STATIC websites only: write raw files into a NEW draft head version.POST /v1/websites/{id}/deploy/json source.write
READget_source_summary{ websiteId }
headVersion, publishedVersion, hasUnpublishedChanges, file count, urls.GET /v1/websites/{id}/source/summary source.read
READlist_source_files{ websiteId, target?, version?, prefix? }
List files in a version (default head).GET /v1/websites/{id}/source/files source.read
READread_source_files{ websiteId, paths, target?, version? }
Read up to 50 files from a version.POST /v1/websites/{id}/source/files/read source.read
WRITEdelete_source_files{ websiteId, paths, expectedHeadVersion?, message? }
Remove files from head as a new version.DELETE /v1/websites/{id}/source/files source.write
READlist_versions{ websiteId, limit?, cursor? }
Version history, newest first.GET /v1/websites/{id}/source/versions source.read
READlist_changes{ websiteId, from?, to? }
Files added/removed/modified between two versions (defaults: from=published, to=head).GET /v1/websites/{id}/source/changes source.read
WRITErestore_head{ websiteId, to }
Copy 'published' or a version number into a new head WITHOUT publishing.POST /v1/websites/{id}/head/restore source.write

Deployments

READget_deployment_status{ websiteId, target?, version? }
Build state for head or published: status (building|ready|failed), canPublish, routes.GET /v1/websites/{id}/deployments/status source.read
WRITEensure_deployment{ websiteId, target?, version? }
Start building a version ahead of time (head by default) so publish_website does not have to wait.POST /v1/websites/{id}/deployments/ensure source.write

Framework source

WRITEacquire_edit_lease{ websiteId }
Acquire the single edit lease for a framework website.POST /v1/websites/{id}/source/lease source.write
READlist_source_tree{ websiteId, type?, nameGlob?, lines?, target?, version? }
List typed source files of a framework website: pages (definePage), components, modules, and singletons (global_css, not_found, website_config, custom_head_*/custom_body_*).GET /v1/websites/{id}/source/tree source.read
READread_source_file{ websiteId, files, target?, version? }
Read up to 50 typed source files (content, bytes, lines, hash) from head, published, or a version.POST /v1/websites/{id}/source/file/read source.read
WRITEput_source_file{ websiteId, leaseToken, file, content, message? }
Create or fully replace one source file; commits a new head version and starts a background preview build.PUT /v1/websites/{id}/source/file source.write
WRITEstr_replace{ websiteId, leaseToken?, files, dryRun? }
Atomic exact-string edits across up to 50 files / 200 edits: each oldStr must match exactly once (or set replaceAll).POST /v1/websites/{id}/source/str-replace source.write
DELETEdelete_source_file{ websiteId, leaseToken, file }
Delete one source file as a new head version (history keeps it).DELETE /v1/websites/{id}/source/file source.write
WRITEset_page_route{ websiteId, leaseToken, page, route }
Change a page's route literal (e.g.PATCH /v1/websites/{id}/source/pages/route source.write
WRITEset_metadata{ websiteId, leaseToken, page?, metadata }
Replace a page's metadata object (title, description, openGraph, ...) or, without page, the site defaults in website_config.PATCH /v1/websites/{id}/source/metadata source.write
READsearch_source{ websiteId, pattern, isRegex?, caseSensitive?, nameGlob?, type?, contextLines?, maxMatchesPerFile?, filesOnly?, target?, version? }
Search source files (literal by default, isRegex for regex).POST /v1/websites/{id}/source/search source.read
READdiff_source_file{ websiteId, type, name?, from?, to? }
Unified diff of one file between two versions (defaults: from=published, to=head).GET /v1/websites/{id}/source/file/diff source.read
READcheck_head{ websiteId, target?, version? }
Run the quality gate on head without changing anything: lint (imports, structure) → typecheck against the generated @webly types → bundle → server-render every page with placeholder CMS content.POST /v1/websites/{id}/head/check source.read

Publishing

WRITErebuild_website{ websiteId, target?, version? }
Re-render a built version (published by default) with the current CMS content.POST /v1/websites/{id}/rebuild publishing.manage
WRITEpublish_website{ websiteId }
Publish the head version to the live site.POST /v1/websites/{id}/publish publishing.manage
DELETEunpublish_website{ websiteId }
Take the site offline on every hostname.POST /v1/websites/{id}/unpublish publishing.manage
WRITErollback_website{ websiteId, version }
Re-publish an earlier version.POST /v1/websites/{id}/rollback publishing.manage

Analytics

READget_analytics{ websiteId, period?, from?, to?, interval?, limit? }
Page views and unique visitors for a website: totals, a time series, and top pages, referrers, countries, and devices.GET /v1/websites/{id}/analytics analytics.read
READquery_analytics{ websiteId, startAt, endAt, dimension?, limit?, path?, referrer?, country?, device?, browser?, os?, hostname?, event?, utmSource?, utmCampaign? }
Engagement totals for a range: pageviews, visitors, visits, bounces, totaltime.GET /v1/websites/{id}/analytics/query analytics.read
READget_analytics_timeseries{ websiteId, startAt, endAt, series?, unit?, timezone?, maxPoints?, compare? }
Traffic over time.GET /v1/websites/{id}/analytics/timeseries analytics.read
READget_realtime_analytics{ websiteId }
Who is on the site right now: views, visitors, events and countries over the trailing 30 minutes.GET /v1/websites/{id}/analytics/realtime analytics.read

Pages

WRITEcreate_page{ websiteId, path, title?, content?, published? }
Legacy: create a DB-backed page record.POST /v1/websites/{id}/pages pages.write
READlist_pages{ websiteId, limit?, cursor? }
List page records of a website.GET /v1/websites/{id}/pages pages.read
READget_page{ websiteId, pageId }
Read one page record.GET /v1/websites/{id}/pages/{pageId} pages.read
WRITEupdate_page{ websiteId, pageId, title?, content?, published? }
Update a page record.PATCH /v1/websites/{id}/pages/{pageId} pages.write
DELETEdelete_page{ websiteId, pageId }
Delete a page record.DELETE /v1/websites/{id}/pages/{pageId} pages.write

Blog

WRITEadd_blog_post{ websiteId, title, body, excerpt?, date?, tags?, cover?, slug?, publish?, publishAt? }
Deprecated: Webly is retiring the CMS and managed blog, and the dashboard no longer shows this content.POST /v1/websites/{id}/blog/posts cms.items.write
WRITEupdate_blog_post{ websiteId, post, title?, body?, excerpt?, date?, tags?, cover?, slug?, publish?, publishAt? }
Deprecated: Webly is retiring the CMS and managed blog, and the dashboard no longer shows this content.PATCH /v1/websites/{id}/blog/posts/{postId} cms.items.write
READlist_blog_posts{ websiteId, status?, limit? }
List the website's blog posts with URLs and status.GET /v1/websites/{id}/blog/posts cms.items.read
READget_blog{ websiteId }
Does this site have a blog, and does the site actually render it? Returns the collection, whether any page reads it, post counts and URLs.GET /v1/websites/{id}/blog cms.items.read

CMS

READget_schema{ websiteId }
Full CMS schema (all collections and fields).GET /v1/websites/{id}/schema cms.schema.read
WRITEcreate_collection{ websiteId, displayName, slug?, description?, fields? }
Deprecated: Webly is retiring the CMS, and the dashboard no longer shows this content.POST /v1/websites/{id}/collections cms.schema.write
READlist_collections{ websiteId, limit?, cursor? }
List CMS collections.GET /v1/websites/{id}/collections cms.schema.read
READget_collection{ websiteId, collectionId }
Get a collection with its fields.GET /v1/websites/{id}/collections/{colId} cms.schema.read
WRITEadd_field{ websiteId, collectionId, key, type, displayName, required?, isUnique?, config? }
Add a field to a collection.POST /v1/websites/{id}/collections/{colId}/fields cms.schema.write
WRITEupdate_field{ websiteId, fieldId, displayName?, required?, isUnique?, config? }
Rename a field or change required/unique/config.PATCH /v1/websites/{id}/fields/{fieldId} cms.schema.write
DELETEdelete_field{ websiteId, fieldId }
Delete a field and its values from every item.DELETE /v1/websites/{id}/fields/{fieldId} cms.schema.write
WRITEupdate_collection{ websiteId, collectionId, displayName?, description? }
Rename a collection or change its description (slug is immutable).PATCH /v1/websites/{id}/collections/{colId} cms.schema.write
DELETEdelete_collection{ websiteId, collectionId }
Delete a collection with all of its fields and items.DELETE /v1/websites/{id}/collections/{colId} cms.schema.write
WRITEcreate_item{ websiteId, collectionId, data, slug?, publishedAt?, publishAt? }
Deprecated: Webly is retiring the CMS and managed blog, and the dashboard no longer shows this content.POST /v1/websites/{id}/collections/{colId}/items cms.items.write
READlist_items{ websiteId, collectionId, published?, search?, filter?, filterLogic?, sort?, limit?, cursor?, depth? }
List items in a collection with search/filter/sort/depth.GET /v1/websites/{id}/collections/{colId}/items cms.items.read
READget_item{ websiteId, itemId, depth? }
Get a single item by ID.GET /v1/websites/{id}/items/{itemId} cms.items.read
READfind_item{ websiteId, collectionId, slug, depth? }
Find an item by collectionId + slug.GET /v1/websites/{id}/items/find cms.items.read
WRITEupdate_item{ websiteId, itemId, data?, slug?, position? }
Patch an item (data keys are merged and validated).PATCH /v1/websites/{id}/items/{itemId} cms.items.write
DELETEdelete_item{ websiteId, itemId }
Soft-delete an item.DELETE /v1/websites/{id}/items/{itemId} cms.items.write
WRITErestore_item{ websiteId, itemId }
Restore a soft-deleted item.POST /v1/websites/{id}/items/{itemId}/restore cms.items.write
WRITEpublish_item{ websiteId, itemId, published?, publishAt? }
Publish now (published=true), schedule (publishAt), or unpublish (published=false) an item.POST /v1/websites/{id}/items/{itemId}/set-published cms.items.write
READget_backreferences{ websiteId, itemId, depth? }
Items that reference this item through reference/multi_reference fields.GET /v1/websites/{id}/items/{itemId}/backreferences cms.items.read

Assets

READlist_assets{ websiteId, kind?, category?, search?, limit?, cursor? }
List or search ready assets (kind, category, search).GET /v1/websites/{id}/assets assets.read
WRITEbegin_asset_upload{ websiteId, filename, mimeType, byteSize, description?, category?, altText? }
Step 1 of 2: register an asset and get a presigned PUT URL.POST /v1/websites/{id}/assets/uploads assets.write
WRITEfinalize_asset_upload{ websiteId, assetId }
Step 2 of 2: verify the uploaded bytes and mark the asset ready.POST /v1/websites/{id}/assets/{assetId}/finalize assets.write
READget_asset{ websiteId, assetId }
Get an asset.GET /v1/websites/{id}/assets/{assetId} assets.read
WRITEupdate_asset{ websiteId, assetId, description?, category?, altText? }
Update an asset's description, category, or alt text.PATCH /v1/websites/{id}/assets/{assetId} assets.write
DELETEdelete_asset{ websiteId, assetId, permanent? }
Archive an asset (existing URLs keep serving, and it keeps counting toward storage).DELETE /v1/websites/{id}/assets/{assetId} assets.write
WRITEimport_assets{ websiteId, items }
Import files that already live on the public web (stock photos, a client's logo, brand assets) as ready assets in one call — no upload dance.POST /v1/websites/{id}/assets/import assets.write
WRITEcreate_image_variant{ websiteId, assetId, width?, height?, fit?, format?, quality? }
Eagerly produce a resized/re-encoded variant of an image asset and return its URLs.POST /v1/websites/{id}/assets/{assetId}/variants assets.write

objects

WRITEbegin_object_upload{ websiteId, files, folder?, expiresIn? }
Share files: upload a batch into one folder of a storage website (create_website kind='storage').POST /v1/websites/{id}/objects/uploads assets.write
WRITEresume_object_upload{ websiteId, objectId }
Continue an upload that was interrupted (dropped connection, restart, a part that kept failing): returns uploadedParts already safely in storage and fresh URLs (with commands) for only the missing parts, or a fresh upload URL for a small file.POST /v1/websites/{id}/objects/{objectId}/resume assets.write
WRITEfinalize_object_upload{ websiteId, ids }
After the bytes are uploaded: check each file landed (and join multipart parts) so its link goes live.POST /v1/websites/{id}/objects/finalize assets.write
READlist_objects{ websiteId, folder?, cursor?, limit? }
List a storage website's files (url, size, expiresAt), optionally in one folder, in path order.GET /v1/websites/{id}/objects assets.read
READlist_archive_entries{ websiteId, objectId }
List the files inside an archive upload (a list_objects item with `archive`), each with its own link.GET /v1/websites/{id}/objects/{objectId}/entries assets.read
DELETEdelete_object{ websiteId, objectId }
Permanently delete a file from a storage website.DELETE /v1/websites/{id}/objects/{objectId} assets.write
DELETEdelete_folder{ websiteId, folder }
Permanently delete every file in a storage website's folder.DELETE /v1/websites/{id}/objects/folders/{folder} assets.write
WRITErename_object{ websiteId, objectId, name }
Rename a file on a storage website as listings and the dashboard show it.PATCH /v1/websites/{id}/objects/{objectId} assets.write
WRITEmove_object{ websiteId, objectId, folder? }
Move a file on a storage website into another folder (existing or new), or omit folder to give it a folder of its own.POST /v1/websites/{id}/objects/{objectId}/move assets.write
WRITElabel_folder{ websiteId, folder, label }
Give a storage website's folder a display name (listed as folderLabel); an empty label clears it.PATCH /v1/websites/{id}/objects/folders/{folder} assets.write

storage

READlist_uploads_in_progress{ websiteId? }
Unfinished uploads across the workspace (storage files and site assets) with how much reached storage, when each was last active, and when it will be cleared.GET /v1/storage/uploads assets.read
DELETEclear_uploads_in_progress{ websiteId? }
Cancel every unfinished upload in the workspace (or one website's), freeing the in-flight allowance.DELETE /v1/storage/uploads assets.write

Forms

READlist_forms{ websiteId }
Forms that have received submissions (name, total, unread, last submission).GET /v1/websites/{id}/forms forms.read
READlist_form_submissions{ websiteId, form, status?, limit?, cursor? }
Submissions of one form, newest first.GET /v1/websites/{id}/forms/{form}/submissions forms.read
READget_form_submission{ websiteId, form, submissionId }
One submission with its full field data (marks it read).GET /v1/websites/{id}/forms/{form}/submissions/{submissionId} forms.read
WRITEupdate_form_submission{ websiteId, form, submissionId, status }
Set a submission's status (new, read, archived, spam).PATCH /v1/websites/{id}/forms/{form}/submissions/{submissionId} forms.write
DELETEdelete_form_submission{ websiteId, form, submissionId }
Permanently delete a submission.DELETE /v1/websites/{id}/forms/{form}/submissions/{submissionId} forms.write

Billing

READget_billing{ none }
The workspace's Webly plan (free/base/max), how many websites it uses of its limit, and the plans on offer.GET /v1/billing billing.read

Domains

WRITEadd_domain{ websiteId, hostname }
Connect a custom domain the owner already owns.POST /v1/websites/{id}/domains domains.write
READlist_domains{ websiteId }
List the platform subdomain and every custom domain with status and pending DNS records.GET /v1/websites/{id}/domains domains.read
READget_domain{ websiteId, domainId }
One custom domain with status, failure message, pending DNS records and `next` (current setup step and what to do).GET /v1/websites/{id}/domains/{domainId} domains.read
WRITEverify_domain{ websiteId, domainId }
Re-check DNS and the HTTPS certificate for a custom domain now.POST /v1/websites/{id}/domains/{domainId}/verify domains.write
WRITEset_primary_domain{ websiteId, domainId }
Make an active custom domain canonical; all other hostnames 308-redirect to it.POST /v1/websites/{id}/domains/{domainId}/primary domains.write
DELETEremove_domain{ websiteId, domainId }
Remove a custom domain.DELETE /v1/websites/{id}/domains/{domainId} domains.write

Me

READwhoami{ none }
The authenticated key's organization, access scope, and capabilities.GET /v1/me websites.read

API keys

WRITEcreate_api_key{ name?, access, expiresAt? }
Create a scoped API key.POST /v1/api-keys apiKeys.manage
READlist_api_keys{ websiteId?, status?, limit?, cursor? }
List API keys (metadata only).GET /v1/api-keys apiKeys.manage
READget_api_key{ keyId }
Metadata for one API key (never the secret).GET /v1/api-keys/{keyId} apiKeys.manage
WRITEupdate_api_key{ keyId, name?, expiresAt?, access? }
Rename a key, change its expiry (null removes it) or its access scope.PATCH /v1/api-keys/{keyId} apiKeys.manage
DELETErotate_api_key{ keyId }
Replace a key's secret.POST /v1/api-keys/{keyId}/rotate apiKeys.manage
DELETErevoke_api_key{ keyId }
Permanently revoke an API key.DELETE /v1/api-keys/{keyId} apiKeys.manage

Audit log

READlist_audit_log{ eventType?, websiteId?, actorId?, limit?, cursor? }
Organization audit trail: publishes, rollbacks, domain changes, key activity.GET /v1/audit-log auditLog.read