Properties
| Name | Type | Description |
|---|---|---|
| id | string | Stable site id (UUID). Unlike the slug, it never changes. |
| slug | string | |
| title | stringnull | |
| keyConvention | string | |
| backend | string | |
| status | string | |
| editingMode | string | |
| publishedAt | numbernull | |
| publicUrl | string | Canonical public site URL for this slug. |
| liveUrl | string | Where visitors reach the site: the connected primary custom domain when there is one, else the same as publicUrl. |
| liveReady | boolean | True after first publish; until then the public URL 404s. |
| build | objectnull | Active clone/build progress, or null when no build is running. |
| building | boolean | build != null && build.phase !== 'done' — clone/build still running. |
| buildPhase | stringnull | Current build phase, or null when no build is active. |
| editorReady | boolean | !building && stats.pages > 0 — draft/editor is renderable. Distinct from liveReady (public URL). |
| createdAt | number | |
| updatedAt | number | |
| stats | object | |
| pages | array | |
| pagesListTotal | number | Total HTML pages matching pagesQ (or all pages when pagesQ is omitted). |
| nextPagesCursor | stringnull | Present when more HTML pages exist — pass as pagesCursor on the next call. |
| pagesPartial | boolean | True when `pages` is one window of the page-key space, not all of it (mirrors /pages's own `partial`) — follow nextPagesCursor rather than treating pagesListTotal as exact. |
| statsPartial | boolean | True when stats.pages/pagesListTotal is a floor from the KV arm's bounded meta-prefix probe, not an exact count. Distinct from pagesPartial, which is about the returned page WINDOW rather than the cou |
| activity | array | |
| needsYou | integer | Only with `?notifications=1` when notifications are enabled: open Needs-you count for this site and the signed-in person (not API keys). |
| brief | objectnull | Only with `?brief=1`: the head of the site brief (purpose, audience, priorities…), or null when none is set. |
| guidance | object | Only with `?brief=1`: the site's rules & guidelines for AI agents — each line (a Rule dotsy checks at publish, or a Guideline), `check` params on Rules (long lists truncated with `more`), short note p |
| draftLog | array | |
| integrations | object | Pending clone integrations stripped at import (0.27.1). |
| mirroredAssets | object | Mirrored external CDN warm progress (assetMirror:full), cumulative across every generation this slug has ever warmed. Bytes land in raw/_cdn/ — not counted in stats.assets or list_assets. |
| previewReady | boolean | Pages staged — preview/publish allowed while asset warm may still run. |
| assetsReady | boolean | Import complete and external warm settled with zero failures. False when the latest warm aborted, even with 0 failures recorded (see mirroredAssets.abort). |
| mirrorComplete | boolean | Alias of assetsReady. |
| failedAssetUrls | array | Sample failed external warm URLs from the latest warm job. |
| assetsHint | string | Present with mirroredAssets — explains CMS assets/ vs mirrored raw/_cdn/ lanes. |
| aiTrain | boolean | Whether AI training crawlers may use the live site. false asks them not to. true when the site has not turned training off. Omitted on the shell payload. |
| ownRobots | boolean | True when an uploaded robots.txt is served on the live site (distinct from the generated file). Omitted on the shell payload. |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://artifacts.apis.io/dotsy-ai/json-schema/dotsy-ai-site-schema.json",
"title": "Site",
"x-generated": "2026-11-06",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/dotsy-ai-openapi.yml#/components/schemas/Site",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Stable site id (UUID). Unlike the slug, it never changes."
},
"slug": {
"type": "string"
},
"title": {
"type": [
"string",
"null"
]
},
"keyConvention": {
"type": "string",
"enum": [
"flat",
"directory"
]
},
"backend": {
"type": "string",
"enum": [
"kv",
"do-sqlite"
]
},
"status": {
"type": "string",
"enum": [
"published",
"draft"
]
},
"editingMode": {
"type": "string",
"enum": [
"live",
"staged"
]
},
"publishedAt": {
"type": [
"number",
"null"
]
},
"publicUrl": {
"type": "string",
"description": "Canonical public site URL for this slug."
},
"liveUrl": {
"type": "string",
"description": "Where visitors reach the site: the connected primary custom domain when there is one, else the same as publicUrl."
},
"liveReady": {
"type": "boolean",
"description": "True after first publish; until then the public URL 404s."
},
"build": {
"type": [
"object",
"null"
],
"properties": {
"jobId": {
"type": "string"
},
"phase": {
"type": "string",
"enum": [
"pages",
"assets",
"done",
"failed"
]
},
"pagesDone": {
"type": "number"
},
"pagesTotal": {
"type": "number"
},
"pagesLanded": {
"type": "number"
},
"updatedAt": {
"type": "number"
}
},
"required": [
"jobId",
"phase",
"pagesDone",
"pagesTotal",
"pagesLanded",
"updatedAt"
],
"description": "Active clone/build progress, or null when no build is running."
},
"building": {
"type": "boolean",
"description": "build != null && build.phase !== 'done' — clone/build still running."
},
"buildPhase": {
"type": [
"string",
"null"
],
"enum": [
"pages",
"assets",
"done",
"failed"
],
"description": "Current build phase, or null when no build is active."
},
"editorReady": {
"type": "boolean",
"description": "!building && stats.pages > 0 — draft/editor is renderable. Distinct from liveReady (public URL)."
},
"createdAt": {
"type": "number"
},
"updatedAt": {
"type": "number"
},
"stats": {
"type": "object",
"properties": {
"pages": {
"type": "number"
},
"assets": {
"type": "number",
"description": "CMS uploads under assets/ only — not mirrored clone CDN bytes (see mirroredAssets)."
},
"storageBytes": {
"type": "number"
},
"lastBuild": {
"type": [
"number",
"null"
]
}
},
"required": [
"pages",
"assets",
"storageBytes",
"lastBuild"
]
},
"pages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"route": {
"type": "string"
},
"updatedAt": {
"type": "number"
}
},
"required": [
"key",
"name",
"route",
"updatedAt"
]
}
},
"pagesListTotal": {
"type": "number",
"description": "Total HTML pages matching pagesQ (or all pages when pagesQ is omitted)."
},
"nextPagesCursor": {
"type": [
"string",
"null"
],
"description": "Present when more HTML pages exist — pass as pagesCursor on the next call."
},
"pagesPartial": {
"type": "boolean",
"description": "True when `pages` is one window of the page-key space, not all of it (mirrors /pages's own `partial`) — follow nextPagesCursor rather than treating pagesListTotal as exact."
},
"statsPartial": {
"type": "boolean",
"description": "True when stats.pages/pagesListTotal is a floor from the KV arm's bounded meta-prefix probe, not an exact count. Distinct from pagesPartial, which is about the returned page WINDOW rather than the count."
},
"activity": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"published",
"updated",
"created"
]
},
"label": {
"type": "string"
},
"at": {
"type": "number"
}
},
"required": [
"kind",
"label",
"at"
]
}
},
"needsYou": {
"type": "integer",
"description": "Only with `?notifications=1` when notifications are enabled: open Needs-you count for this site and the signed-in person (not API keys)."
},
"brief": {
"type": [
"object",
"null"
],
"properties": {
"purpose": {
"type": "string",
"minLength": 5,
"maxLength": 280
},
"audience": {
"type": "string",
"maxLength": 200
},
"kind": {
"type": "string",
"enum": [
"exploration",
"production"
]
},
"priorities": {
"type": "object",
"properties": {
"seo": {
"type": "string",
"enum": [
"must",
"nice",
"skip"
]
},
"analytics": {
"type": "string",
"enum": [
"must",
"nice",
"skip"
]
},
"design": {
"type": "string",
"enum": [
"must",
"nice",
"skip"
]
},
"mobile": {
"type": "string",
"enum": [
"must",
"nice",
"skip"
]
}
}
},
"editingModel": {
"type": "string",
"enum": [
"ai",
"simple-editor",
"creator"
]
},
"design": {
"type": "object",
"properties": {
"palette": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 8
},
"type": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 6
},
"layout": {
"type": "string",
"maxLength": 80
}
},
"additionalProperties": false
},
"hasBody": {
"type": "boolean"
},
"businessName": {
"type": "string"
}
},
"required": [
"purpose",
"hasBody"
],
"description": "Only with `?brief=1`: the head of the site brief (purpose, audience, priorities…), or null when none is set."
},
"guidance": {
"type": "object",
"properties": {
"rules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z0-9-]{2,40}$"
},
"card": {
"type": "string",
"enum": [
"intent",
"design",
"content",
"seo"
]
},
"text": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"rule",
"guideline"
]
},
"level": {
"type": "string",
"enum": [
"block",
"warn"
]
},
"locked": {
"type": "boolean"
},
"check": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"required": [
"type"
]
}
},
"required": [
"id",
"card",
"text",
"kind",
"locked"
]
}
},
"notes": {
"type": "object",
"properties": {
"design": {
"type": "string",
"maxLength": 300
},
"content": {
"type": "string",
"maxLength": 300
},
"seo": {
"type": "string",
"maxLength": 300
}
}
},
"missing": {
"type": "array",
"items": {
"type": "string",
"enum": [
"design",
"content",
"seo"
]
}
},
"next": {
"type": "string",
"enum": [
"Before your first change: load get_skill('guidance') and write the missing sections."
]
},
"aiAccess": {
"type": "string",
"enum": [
"viewer",
"contributor",
"editor",
"owner"
]
},
"searchLine": {
"type": "string"
}
},
"required": [
"rules",
"notes",
"missing",
"aiAccess"
],
"description": "Only with `?brief=1`: the site's rules & guidelines for AI agents — each line (a Rule dotsy checks at publish, or a Guideline), `check` params on Rules (long lists truncated with `more`), short note previews, which sections are still unwritten (`missing`), optional `next: get_skill('guidance')` when `missing` is non-empty, and the AI access level. Present even when the site has no readable brief (everything `missing`)."
},
"draftLog": {
"type": "array",
"items": {
"type": "object",
"properties": {
"tool": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"context": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"at": {
"type": "number"
}
},
"required": [
"tool",
"context",
"at"
]
}
},
"integrations": {
"type": "object",
"properties": {
"pending": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"vendor": {
"type": "string"
},
"sourceId": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"pending"
]
},
"pageCount": {
"type": "number"
},
"mayAffectUi": {
"type": "boolean"
}
},
"required": [
"id",
"vendor",
"sourceId",
"status",
"pageCount"
]
}
}
},
"required": [
"pending"
],
"description": "Pending clone integrations stripped at import (0.27.1)."
},
"mirroredAssets": {
"type": "object",
"properties": {
"state": {
"type": "string"
},
"warmMode": {
"type": "string"
},
"resolved": {
"type": "boolean",
"description": "Warm has settled (not pending/running) — 'stop polling', NOT 'assets are fine'. state:\"aborted\" is also resolved; check assetsReady/mirrorComplete (already exclude aborted) or the `abort` field before reading this as healthy."
},
"counts": {
"type": "object",
"properties": {
"total": {
"type": "number"
},
"warmed": {
"type": "number"
},
"failed": {
"type": "number"
},
"capped": {
"type": "number"
},
"skipped": {
"type": "number"
}
},
"required": [
"total",
"warmed",
"failed",
"capped",
"skipped"
]
},
"externalCapped": {
"type": "object",
"properties": {
"count": {
"type": "number"
},
"total": {
"type": "number"
},
"ratio": {
"type": "number"
}
},
"required": [
"count",
"total",
"ratio"
],
"description": "Fraction of assets left origin-backed (302 to source, not on dotsy R2) — a fidelity/durability risk even when failed:0. ratio > 0.25 should read as visual PARTIAL minimum per the eval rubric (0.63 §6.4)."
},
"failedAssetUrls": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bounded sample of failed asset source URLs for triage (capped at 50). counts.failed is the true total. Present only when there is something to show."
},
"abort": {
"type": "object",
"properties": {
"stage": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"description": "Present only when state:\"aborted\" — which named phase the run reached and why, so a total abort with 0 failures is never read as a clean success with nothing to do."
},
"generations": {
"type": "number",
"description": "Manifest generations (import/re-import/segment-recovery attempts) folded into `counts`. Omitted for a single-generation site."
}
},
"required": [
"state",
"counts",
"externalCapped"
],
"description": "Mirrored external CDN warm progress (assetMirror:full), cumulative across every generation this slug has ever warmed. Bytes land in raw/_cdn/ — not counted in stats.assets or list_assets."
},
"previewReady": {
"type": "boolean",
"description": "Pages staged — preview/publish allowed while asset warm may still run."
},
"assetsReady": {
"type": "boolean",
"description": "Import complete and external warm settled with zero failures. False when the latest warm aborted, even with 0 failures recorded (see mirroredAssets.abort)."
},
"mirrorComplete": {
"type": "boolean",
"description": "Alias of assetsReady."
},
"failedAssetUrls": {
"type": "array",
"items": {
"type": "string"
},
"description": "Sample failed external warm URLs from the latest warm job."
},
"assetsHint": {
"type": "string",
"description": "Present with mirroredAssets — explains CMS assets/ vs mirrored raw/_cdn/ lanes."
},
"aiTrain": {
"type": "boolean",
"description": "Whether AI training crawlers may use the live site. false asks them not to. true when the site has not turned training off. Omitted on the shell payload."
},
"ownRobots": {
"type": "boolean",
"description": "True when an uploaded robots.txt is served on the live site (distinct from the generated file). Omitted on the shell payload."
}
},
"required": [
"id",
"slug",
"title",
"keyConvention",
"backend",
"status",
"editingMode",
"publishedAt",
"publicUrl",
"liveUrl",
"liveReady",
"build",
"building",
"buildPhase",
"editorReady",
"createdAt",
"updatedAt",
"stats",
"pages",
"pagesListTotal",
"nextPagesCursor",
"activity"
]
}
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/dotsy-ai-site"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.