{"openapi":"3.1.0","info":{"title":"smartcms Content API","version":"2","description":"Public REST contract for smartcms's content APIs: the read-only Delivery API (`v1`, `cda_`/`cpa_` keys) and the read/write Management API (`v2`, `cma_` keys). The same routes are reachable on two hosts with two different base paths (verified live 2026-09-19): the public API host (`api.smartcms.ai`, Bunny edge in front) serves them directly under `/content/v{1,2}/...`; the app host (`www.smartcms.ai`) serves the identical routes under `/api/content/v{1,2}/...`. Pick whichever `servers` entry matches how you call the API — the path suffix after the base path is identical either way."},"servers":[{"url":"https://api.smartcms.ai","description":"Public API host (Bunny edge in front)"},{"url":"https://www.smartcms.ai/api","description":"App host, same routes under /api"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"smartcms API key. Three prefixes gate three independent capability sets (`ApiKeyType`, `api-key.ts`):\n- `cda_` — delivery: read-only, published content only.\n- `cpa_` — preview: read-only, includes draft/unpublished content.\n- `cma_` — management: full read/write within the key's space + environment scope (no finer-grained role model — a valid `cma_` key IS the authorization for its scope, Contentful-style).\nSend as `Authorization: Bearer <key>`, `x-api-key: <key>`, or `?access_token=<key>` (in that priority order; `extractToken`, `api-key.ts`)."}},"responses":{"Unauthorized":{"description":"Missing/invalid API key, or the key's type is not allowed for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Forbidden":{"description":"The key is not scoped to this space, or (v2) not scoped to this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found, or not visible to this key's space/environment scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Conflict":{"description":"State conflict — e.g. a tag name/slug collision, or a schedule that was already cancelled/executed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"LockedByMigration":{"description":"The content type is mid content-model-migration (assertContentTypeNotMigrating). Retry after the migration referenced by `migrationId` completes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"RateLimited":{"description":"Rate limit exceeded for this key/space tier (chargeDeliveryRateLimit). See the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message."},"validationErrors":{"description":"Present on 400 field-validation failures (record create/update/publish). Shape follows the content type's field validation rules; not a fixed schema."},"code":{"type":"string","description":"Stable machine-readable error code, currently only 'content_type_migrating' (423, set by assertContentTypeNotMigrating)."},"migrationId":{"type":"string","description":"Set alongside `code` on a 423 migration lock, so the caller can poll migration progress."}},"required":["error"]},"ContentRecordVersion":{"type":"object","properties":{"id":{"type":"string"},"contentRecordId":{"type":"string"},"versionNumber":{"type":"integer"},"data":{"type":"object","additionalProperties":{},"description":"Field data, keyed per the content type's field names. Localized fields store {<locale>: value}; non-localized fields store the value directly. __status/__publishedAt are locale-keyed status maps on this object (see inventory-content-model.md). Top-level __* keys are server-owned and ignored on write; change status via status/statusLocale or the publish/unpublish/archive endpoints — omitting them no longer resets status."},"createdAt":{"type":"string"},"createdBy":{"type":["string","null"]},"notes":{"type":["string","null"]}},"required":["id","contentRecordId","versionNumber","data","createdAt","createdBy","notes"]},"ContentRecord":{"type":"object","properties":{"id":{"type":"string"},"contentTypeId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"activeVersion":{"type":"integer"},"latestVersion":{"type":"integer"},"draftRevision":{"type":"integer","description":"Optimistic-lock counter bumped on every autosave write, including in-place edits that don't move latestVersion."},"clientDraftKey":{"type":["string","null"]},"currentVersion":{"$ref":"#/components/schemas/ContentRecordVersion"}},"required":["id","contentTypeId","createdAt","updatedAt","activeVersion","latestVersion","draftRevision","clientDraftKey","currentVersion"]},"ScheduledPublish":{"type":"object","properties":{"id":{"type":"string"},"contentRecordId":{"type":"string"},"locale":{"type":"string"},"publishAt":{"type":"string"},"action":{"type":"string","enum":["publish","unpublish"]},"status":{"type":"string","enum":["pending","executing","done","cancelled","failed"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"createdBy":{"type":["string","null"]},"executedAt":{"type":["string","null"]},"failureReason":{"type":["string","null"]}},"required":["id","contentRecordId","locale","publishAt","action","status","createdAt","updatedAt","createdBy","executedAt","failureReason"]},"Environment":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":["string","null"]},"isDefault":{"type":"boolean"},"spaceId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","slug","description","isDefault","spaceId","createdAt","updatedAt"]},"Locale":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"spaceId":{"type":"string"}},"required":["id","code","name","isDefault","isActive","createdAt","updatedAt","spaceId"]},"ContentType":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":["string","null"]},"fields":{"type":"array","items":{"type":"object","additionalProperties":{}}},"fieldSchema":{},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"environmentId":{"type":"string"}},"required":["id","name","displayName","description","fields","createdAt","updatedAt","environmentId"]},"SerializedMigration":{"type":"object","properties":{"id":{"type":"string"},"contentTypeId":{"type":"string"},"kind":{"type":"string","enum":["migrate","rollback"]},"status":{"type":"string","enum":["planned","running","completed","failed","rolledBack"]},"totalRecords":{"type":"integer"},"processedRecords":{"type":"integer"},"migratedRecords":{"type":"integer"},"skippedRecords":{"type":"integer"},"failedRecords":{"type":"integer"},"startedBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"finishedAt":{"type":["string","null"]},"error":{"type":["string","null"]},"rollbackOfId":{"type":["string","null"]},"rollbackId":{"type":["string","null"],"description":"Id of the kind:\"rollback\" row that reverses this migration, if any."}},"required":["id","contentTypeId","kind","status","totalRecords","processedRecords","migratedRecords","skippedRecords","failedRecords","startedBy","createdAt","updatedAt","finishedAt","error","rollbackOfId","rollbackId"]},"ReleaseItem":{"type":"object","properties":{"id":{"type":"string"},"releaseId":{"type":"string"},"contentRecordId":{"type":"string"},"locales":{"type":"array","items":{"type":"string"}},"addedBy":{"type":["string","null"]},"addedAt":{"type":"string"},"lastResult":{"type":["string","null"]},"lastError":{"type":["string","null"]}},"required":["id","releaseId","contentRecordId","locales","addedBy","addedAt","lastResult","lastError"]},"Release":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"spaceId":{"type":"string"},"environmentId":{"type":"string"},"status":{"type":"string","enum":["open","scheduled","executing","done","failed"],"description":"No \"cancelled\" status exists — cancelling a schedule resets to \"open\"; deleting removes the row."},"scheduledFor":{"type":["string","null"]},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"executedAt":{"type":["string","null"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/ReleaseItem"},"description":"Present on GET .../releases/{id} only."},"_count":{"type":"object","properties":{"items":{"type":"integer"}},"required":["items"],"description":"Present on GET .../releases (list) only."}},"required":["id","title","description","spaceId","environmentId","status","scheduledFor","createdBy","createdAt","updatedAt","executedAt"]},"ReleaseRunReport":{"type":"object","properties":{"releaseId":{"type":"string"},"status":{"type":"string","enum":["open","scheduled","executing","done","failed"]},"items":{"type":"array","items":{"type":"object","properties":{"itemId":{"type":"string"},"contentRecordId":{"type":"string"},"result":{"type":"string","enum":["ok","skipped","failed"]},"locales":{"type":"array","items":{"type":"string"}},"message":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"locale":{"type":"string"},"message":{"type":"string"}},"required":["field","message"]}}},"required":["itemId","contentRecordId","result"]}},"publishedCount":{"type":"integer"},"skippedCount":{"type":"integer"},"failedCount":{"type":"integer"}},"required":["releaseId","status","items","publishedCount","skippedCount","failedCount"]},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"format":{"type":"string","description":"\"raw\" (default, {event,payload,delivered_at}) or \"slack\" ({text})."},"active":{"type":"boolean"},"events":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"environmentId":{"type":"string"},"hasSecret":{"type":"boolean","description":"Whether a signing secret is configured. The secret value itself is never returned."},"environment":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"space":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"required":["id","name","organization"]}},"required":["id","name","space"]}},"required":["id","name","url","format","active","events","createdAt","updatedAt","environmentId","hasSecret","environment"]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"webhookId":{"type":"string"},"event":{"type":"string"},"isTest":{"type":"boolean"},"status":{"type":"string","enum":["success","failed","skipped"]},"httpStatus":{"type":["integer","null"],"description":"null when no HTTP response was received (timeout, DNS, abort)."},"latencyMs":{"type":["integer","null"]},"attempt":{"type":"integer"},"errorMessage":{"type":["string","null"]},"requestBody":{"type":["string","null"],"description":"Truncated to ~16 KB."},"responseBody":{"type":["string","null"],"description":"Truncated to ~16 KB."},"deliveredAt":{"type":"string"}},"required":["id","webhookId","event","isTest","status","httpStatus","latencyMs","attempt","errorMessage","requestBody","responseBody","deliveredAt"]},"ProjectedAsset":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":["string","null"]},"title":{"type":["string","null"]},"altText":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"aiGenerated":{"type":"boolean","description":"AI Act Art. 50(2) provenance marker — derived server-side: true when the asset carries the `ai-generated` tag OR `localizedMeta.aiGenerated === true` (isAiGeneratedAsset)."},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"spaceId":{"type":"string"},"organizationId":{"type":["string","null"]},"url":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"files":{"type":"object","additionalProperties":{"type":"object","properties":{"url":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]}},"required":["url","filename","contentType","size","width","height"]},"description":"Locale-variant files, keyed by locale code."},"defaultLocale":{"type":"string"}},"required":["id","description","title","altText","tags","aiGenerated","createdAt","updatedAt","spaceId","organizationId","url","filename","contentType","size","width","height","files","defaultLocale"]},"Asset":{"type":"object","properties":{"id":{"type":"string"},"filename":{"type":"string"},"url":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"description":{"type":["string","null"]},"title":{"type":["string","null"]},"altText":{"type":["string","null"]},"focalX":{"type":["number","null"],"description":"Normalized 0..1 crop focal point X. null = no focal point set."},"focalY":{"type":["number","null"]},"localizedMeta":{"description":"{ <locale>: { title?, description?, altText? } }"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"spaceId":{"type":"string"},"organizationId":{"type":["string","null"]}},"required":["id","filename","url","contentType","size","width","height","description","title","altText","focalX","focalY","tags","createdAt","updatedAt","spaceId","organizationId"]},"AssetFile":{"type":"object","properties":{"id":{"type":"string"},"assetId":{"type":"string"},"locale":{"type":"string"},"url":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","assetId","locale","url","filename","contentType","size","width","height","createdAt","updatedAt"]},"Tag":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"spaceId":{"type":"string"},"usageCount":{"type":"integer","description":"Number of content records this tag is attached to."},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","slug","spaceId","usageCount","createdAt","updatedAt"]},"DeliveryContentType":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"displayName":{"type":["string","null"]},"description":{"type":["string","null"]},"fields":{"type":"array","items":{"type":"object","additionalProperties":{}}},"environmentId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","displayName","description","fields","environmentId","createdAt","updatedAt"]},"DeliveryAsset":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"files":{"type":"object","additionalProperties":{"type":"object","properties":{"url":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]}},"required":["url","filename","contentType","size","width","height"]},"description":"Locale-variant files, keyed by locale code."},"defaultLocale":{"type":"string"},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"aiGenerated":{"type":"boolean","description":"AI Act Art. 50(2) provenance marker — derived server-side: true when the asset carries the `ai-generated` tag OR `localizedMeta.aiGenerated === true` (isAiGeneratedAsset)."}},"required":["id","url","filename","contentType","size","width","height","files","defaultLocale","title","description","tags","aiGenerated"]}},"parameters":{}},"paths":{"/content/v2/health":{"get":{"summary":"Liveness check (v2)","tags":["Health"],"security":[],"x-smartcms-key-types":[],"responses":{"200":{"description":"Always 200 while the app process is up. Sent with `Cache-Control: no-store` so the CDN never answers a liveness probe from cache.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string","enum":["v2"]},"timestamp":{"type":"string","description":"ISO-8601 timestamp of the response."}},"required":["status","version","timestamp"]}}}}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records":{"get":{"summary":"List content records","description":"A `delivery` key only ever sees published content (forced status:\"published\" inside findContentRecordsByEnvironment; an explicit non-published `status` filter from a delivery key is rejected). `preview`/`management` keys see every status. Items get the same delivery projection as GET .../content-records/{id} and v1's list (projectDeliveryListItems): `?locale=` flattens each item; `?include=` additionally resolves references inline.","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Filter by ContentType.name."},"required":false,"name":"contentType","in":"query"},{"schema":{"type":"string","enum":["draft","published","archived"],"description":"Filter by record status. A `delivery` key is always forced to \"published\" server-side; an explicit non-published value from a delivery key is rejected (403)."},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Projects each item into that locale's flattened shape."},"required":false,"name":"locale","in":"query"},{"schema":{"type":"string","description":"Reference-resolution depth (only with locale set)."},"required":false,"name":"include","in":"query"},{"schema":{"type":"string","description":"Clamped server-side (parsePagination)."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"},{"schema":{"type":"string","description":"field or -field; only updatedAt/createdAt are supported (v1 REST order constraint, reused here)."},"required":false,"name":"order","in":"query"}],"responses":{"200":{"description":"Matching records.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"400":{"description":"Unknown query parameter; an unsupported `status`; a bad fields.<name>.<op> filter (WhereClauseError); or a typed fields.* filter without `contentType` (field types can't be resolved without a content type to resolve them against).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"A typed fields.* filter named a `contentType` that doesn't exist in this environment (a plain status/list filter with no such content type instead just yields an empty page, not a 404).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create a content record","description":"Creates the record and its first version (versionNumber 1) atomically. 403 if contentTypeId belongs to a different space/environment than the URL names (loadContentTypeScoped) — distinct from the generic key-scope 403. 423 if the content type is mid content-model-migration (assertContentTypeNotMigrating).","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contentTypeId":{"type":"string"},"data":{"type":"object","additionalProperties":{},"description":"Field data, keyed per the content type's field names."},"status":{"type":"string","enum":["draft","published"],"description":"Default: draft."},"initialStatusLocale":{"type":"string","description":"Default: the environment's default locale."},"tagIds":{"type":"array","items":{"type":"string"}}},"required":["contentTypeId","data"]}}}},"responses":{"201":{"description":"Record created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentRecord"}}}},"400":{"description":"Missing contentTypeId/data, or field validation failed (validationErrors).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"contentTypeId does not exist (loadContentTypeScoped).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"423":{"$ref":"#/components/responses/LockedByMigration"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records/{id}":{"get":{"summary":"Get a single content record","description":"?locale= triggers hydration into that locale's flattened shape; without it, the raw multi-locale shape is returned. ?include=<depth> resolves references inline (locale path only). A record belonging to a sibling environment the key can also reach reads as 404 here (this route is scoped to ONE environment by its URL).","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"locale","in":"query"},{"schema":{"type":"string","description":"Reference-resolution depth; only with locale set."},"required":false,"name":"include","in":"query"}],"responses":{"200":{"description":"The record.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Record not found, or belongs to a different space/environment than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"summary":"Update a content record (new version)","description":"Allocates the next version via allocateAndCommitVersion (MAX+1 in a transaction, retried on a P2002 race) and bumps draftRevision, the optimistic-lock counter for autosave conflict detection. 423 if the content type is mid content-model-migration.","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{},"description":"Omitted: keeps the active version's data. Top-level __* keys are server-owned and ignored on write; change status via status/statusLocale or the publish/unpublish/archive endpoints — omitting them no longer resets status."},"status":{"type":"string","enum":["draft","published"]},"statusLocale":{"type":"string","description":"Default: the environment's default locale."},"tagIds":{"type":"array","items":{"type":"string"}},"notes":{"type":"string"},"makeActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Record updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentRecord"}}}},"400":{"description":"Field validation failed (validationErrors).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/LockedByMigration"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete a content record","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Record deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records/{id}/archive":{"put":{"summary":"Archive a content record (one or every known locale)","description":"Sets data.__status[locale] = \"archived\" for every requested locale (or every known locale without ?locale=), then bumps the version (allocateAndCommitVersion) and draftRevision. 400 if there is nothing to archive (?locale= names an already-archived locale, or the record has no locales at all). 423 while the content type is mid content-model-migration.","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Without it, every known locale is archived."},"required":false,"name":"locale","in":"query"}],"responses":{"200":{"description":"Record archived.","content":{"application/json":{"schema":{"type":"object","properties":{"record":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."},"currentVersion":{"$ref":"#/components/schemas/ContentRecordVersion"},"archivedLocales":{"type":"array","items":{"type":"string"},"description":"Locales just moved to \"archived\"."},"message":{"type":"string"}},"required":["record","currentVersion","archivedLocales","message"]}}}},"400":{"description":"Nothing to archive — the record has no locales, or the named locale is already archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/LockedByMigration"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records/{id}/unarchive":{"put":{"summary":"Restore an archived content record to draft","description":"Flips data.__status[locale] back to \"draft\" for every requested locale (or every currently-archived locale without ?locale=), then bumps the version (allocateAndCommitVersion) and draftRevision. 400 if there is nothing archived to restore, or ?locale= names a locale that isn't archived. 423 while the content type is mid content-model-migration.","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Without it, every archived locale is restored."},"required":false,"name":"locale","in":"query"}],"responses":{"200":{"description":"Record unarchived.","content":{"application/json":{"schema":{"type":"object","properties":{"record":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."},"currentVersion":{"$ref":"#/components/schemas/ContentRecordVersion"},"unarchivedLocales":{"type":"array","items":{"type":"string"},"description":"Locales just restored to \"draft\"."},"message":{"type":"string"}},"required":["record","currentVersion","unarchivedLocales","message"]}}}},"400":{"description":"Nothing archived to restore, or the named locale isn't archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/LockedByMigration"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records/{id}/unpublish":{"put":{"summary":"Unpublish a content record (one or every published locale)","description":"Flips data.__status[locale] back to \"draft\" for every requested locale (or every currently-published locale without ?locale=), then bumps the version (allocateAndCommitVersion via commitStatusFlip) and draftRevision. 400 if nothing was actually published. 423 while the content type is mid content-model-migration.","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Without it, every published locale is unpublished."},"required":false,"name":"locale","in":"query"}],"responses":{"200":{"description":"Record unpublished.","content":{"application/json":{"schema":{"type":"object","properties":{"record":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."},"currentVersion":{"$ref":"#/components/schemas/ContentRecordVersion"},"unpublishedLocales":{"type":"array","items":{"type":"string"},"description":"Locales just flipped back to \"draft\"."},"message":{"type":"string"}},"required":["record","currentVersion","unpublishedLocales","message"]}}}},"400":{"description":"Nothing was published — nothing to unpublish.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/LockedByMigration"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records/{id}/publish":{"put":{"summary":"Publish one locale of a content record","description":"Without ?locale=, publishes the space's default locale.","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Default: the space's default locale."},"required":false,"name":"locale","in":"query"}],"responses":{"200":{"description":"Locale published.","content":{"application/json":{"schema":{"type":"object","properties":{"recordId":{"type":"string"},"locale":{"type":"string"},"versionNumber":{"type":"integer"},"previousStatus":{"type":"string","description":"The locale's status before this publish."},"status":{"type":"string","enum":["published"]}},"required":["recordId","locale","versionNumber","previousStatus","status"]}}}},"400":{"description":"Already published for this locale, or field validation failed (validationErrors).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Record not found, or no active version to publish.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-records/{id}/schedule-publish":{"post":{"summary":"Schedule a future publish or unpublish of one locale","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Required."},"required":true,"name":"locale","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"publishAt":{"type":"string","description":"ISO-8601 timestamp, must be in the future."},"action":{"type":"string","enum":["publish","unpublish"],"description":"Default: publish."}},"required":["publishAt"]}}}},"responses":{"200":{"description":"Schedule created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledPublish"}}}},"400":{"description":"Missing locale/publishAt, invalid action, or publishAt not in the future.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"summary":"List pending schedules for a record","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Pending ScheduledPublish rows for this record, ordered by publishAt ascending.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledPublish"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Cancel a pending schedule","tags":["Content Records (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentRecord id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Required."},"required":true,"name":"scheduledId","in":"query"}],"responses":{"200":{"description":"Schedule cancelled.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"description":"Missing scheduledId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Record not found, or the scheduledId doesn't belong to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Schedule was already cancelled/executed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments":{"get":{"summary":"List the space's environments","description":"An allowedEnvironmentIds-scoped key only sees its own environments here, not every environment of the space (applyApiKeyFilters).","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","description":"Clamped server-side (parsePagination)."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Matching environments.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Environment"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create an environment","description":"`spaceId` is taken from the path, not the body. 400 if the slug already exists in this space, or on the plan's maxEnvironments limit / field validation. Setting isDefault:true atomically unsets the space's previous default.","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string","description":"Lowercase, alphanumeric + hyphens. \"master\" is reserved (the default-environment alias)."},"description":{"type":"string"},"isDefault":{"type":"boolean","description":"Default: false."},"cloneFromEnvironmentId":{"type":"string","description":"Enqueues an async clone of this environment's content into the new one (BullMQ, best-effort). 404 if not found/accessible in this space; 503 if the clone could not be queued (job queue unavailable) — the just-created environment is rolled back in that case."},"cloneContentRecords":{"type":"boolean","description":"Only used with cloneFromEnvironmentId. Default: false."}},"required":["name","slug"]}}}},"responses":{"201":{"description":"Environment created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Environment"}}}},"400":{"description":"Slug already exists in this space, plan limit reached, or field validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"cloneFromEnvironmentId not found or not accessible in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Clone requested but could not be queued (job queue unavailable); the created environment was rolled back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}":{"get":{"summary":"Get a single environment","description":"`:environmentId` is the id withApiKey already resolved — the `master` alias is resolved to a concrete id before this handler runs.","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"responses":{"200":{"description":"The environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Environment"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found, or belongs to a different space than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Update an environment","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string","description":"Lowercase, alphanumeric + hyphens. \"master\" is reserved. 400 if already taken by another environment in this space."},"description":{"type":["string","null"]},"isDefault":{"type":"boolean","description":"Setting true atomically unsets the space's previous default."}}}}}},"responses":{"200":{"description":"Environment updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Environment"}}}},"400":{"description":"Slug already taken, or field validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, or belongs to a different space than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete an environment","description":"400 if this is the space's default environment. Cascades ContentRecord → ContentType → Webhook → ApiKey for this environment in one transaction.","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"responses":{"200":{"description":"Environment deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string"}},"required":["deleted","id"]}}}},"400":{"description":"Cannot delete the default environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, or belongs to a different space than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/clone-status":{"get":{"summary":"Poll an environment clone's progress","description":"Reads Redis-backed progress written by the enqueue producer and the env-clone worker — no DB write. management-only: only a management key can start a clone.","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"responses":{"200":{"description":"Current clone status.","content":{"application/json":{"schema":{"type":"object","properties":{"environmentId":{"type":"string"},"progress":{"type":"number","description":"0-100."},"status":{"type":"string","description":"Human-readable status text."},"error":{"type":"string"},"completedAt":{"type":"string"},"startedAt":{"type":"string"},"contentTypesCloned":{"type":"integer"},"totalContentTypes":{"type":"integer"},"recordsCloned":{"type":"integer"},"totalRecords":{"type":"integer"}},"required":["environmentId","progress","status","startedAt","contentTypesCloned","totalContentTypes","recordsCloned","totalRecords"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No cloning status found for this environment (never cloned, or the status entry expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/promote":{"post":{"summary":"Promote an environment to be the space's default","description":"An atomic isDefault flip only — does NOT copy content (see createEnvironment's cloneFromEnvironmentId for that). After this commits, the `master` alias and every delivery/preview request using it resolve to this environment. 400 (code: \"already_default\") if `:environmentId` is already the space's default.","tags":["Environments (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"responses":{"200":{"description":"Environment promoted.","content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"allOf":[{"$ref":"#/components/schemas/Environment"},{"type":["object","null"]}]},"previousDefault":{"allOf":[{"$ref":"#/components/schemas/Environment"},{"type":["object","null"]}]}},"required":["environment","previousDefault"]}}}},"400":{"description":"Already the space's default environment (code: \"already_default\").","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, or belongs to a different space than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/locales":{"get":{"summary":"List the space's locales","description":"Locales are SPACE-scoped, not per-environment (locale-service.ts's module comment) — the :environmentId segment only keeps URL parity with the sibling endpoints and is still validated by withApiKey. Not paginated: limit/skip in the response envelope both equal items.length.","tags":["Locales (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"responses":{"200":{"description":"The space's locales.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Locale"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create a locale","description":"409 if a locale with this code already exists in the space. Setting isDefault:true atomically unsets the space's previous default and best-effort syncs CmsSettings.defaultLocale (the organization-level fallback).","tags":["Locales (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"BCP-47 tag, e.g. de-AT, nl-NL, pt-BR."},"name":{"type":"string"},"isDefault":{"type":"boolean","description":"Default: false."},"isActive":{"type":"boolean","description":"Default: true."}},"required":["code","name"]}}}},"responses":{"201":{"description":"Locale created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Locale"}}}},"400":{"description":"Field validation failed (invalid BCP-47 code, missing name).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"A locale with this code already exists in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/locales/{code}":{"get":{"summary":"Get a single locale by code","tags":["Locales (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"The locale's BCP-47 code (immutable)."},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"The locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Locale"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"No locale with this code in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Update a locale","description":"code is immutable — only name/isDefault/isActive can change.","tags":["Locales (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"The locale's BCP-47 code (immutable)."},"required":true,"name":"code","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"isDefault":{"type":"boolean","description":"Setting true atomically unsets the space's previous default."},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Locale updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Locale"}}}},"400":{"description":"Field validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No locale with this code in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete a locale","description":"400 if this is the space's default locale, or its only locale.","tags":["Locales (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"The locale's BCP-47 code (immutable)."},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"Locale deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"code":{"type":"string"}},"required":["deleted","code"]}}}},"400":{"description":"Cannot delete the default locale, or the only locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No locale with this code in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-types":{"get":{"summary":"List content types","description":"Read access matches v1/GraphQL: a model schema is metadata about the caller's own environment, not protected content, so delivery/preview keys can read it too. Sorted by name asc (not updatedAt) — same stable/diffable order as v1's content-types list.","tags":["Content Types (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","description":"Clamped server-side (parsePagination). Default 1000 (same as v1's content-types list — a model's field-definition count is nowhere near record-list scale)."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Matching content types.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContentType"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create a content type","description":"`environmentId` is taken from the path, not the body. 400 if a content type with this name already exists in the environment, or on field validation failure.","tags":["Content Types (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Must start with a letter, letters/numbers/underscores only. Unique within the environment."},"displayName":{"type":"string"},"description":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean","description":"Default: false."},"displayName":{"type":"string"},"label":{"type":"string"},"localized":{"type":"boolean"},"isTitle":{"type":"boolean"},"technical":{"type":"boolean"},"defaultValue":{},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]}},"validations":{"type":"object","additionalProperties":{}},"settings":{"type":"object","additionalProperties":{}}},"required":["id","name","type"]},"minItems":1},"fieldSchema":{"type":"object","additionalProperties":{}}},"required":["name","displayName","fields"]}}}},"responses":{"201":{"description":"Content type created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}},"400":{"description":"Field validation failed, or a content type with this name already exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-types/{id}":{"get":{"summary":"Get a single content type","tags":["Content Types (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The content type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found, or belongs to a different space/environment than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"summary":"Update a content type","description":"409 from the model-compatibility guard (rename/remove/type-change rules), a concurrent-update race (P2025), or while the content type is mid content-model-migration (assertContentTypeNotMigrating — 409 on this write path, not 423).","tags":["Content Types (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"A rename attempt is always refused (409)."},"displayName":{"type":"string"},"description":{"type":["string","null"]},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"},"displayName":{"type":"string"},"label":{"type":"string"},"localized":{"type":"boolean"},"isTitle":{"type":"boolean"},"technical":{"type":"boolean"},"defaultValue":{},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]}},"validations":{"type":"object","additionalProperties":{}},"settings":{"type":"object","additionalProperties":{}}},"required":["id","name","type"],"description":"REPLACES ContentType.fields wholesale — unknown keys pass through (.passthrough() server-side) rather than being silently dropped."}},"fieldSchema":{"type":"object","additionalProperties":{}}}}}}},"responses":{"200":{"description":"Content type updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}},"400":{"description":"Field validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, or belongs to a different space/environment than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete a content type","description":"409 if any content record still references it (content_type_in_use), or while mid content-model-migration.","tags":["Content Types (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Content type deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string"}},"required":["deleted","id"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, or belongs to a different space/environment than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-types/{id}/migrations":{"get":{"summary":"List a content type's migrations","tags":["Content-Model Migrations (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Recent migrations.","content":{"application/json":{"schema":{"type":"object","properties":{"migrations":{"type":"array","items":{"$ref":"#/components/schemas/SerializedMigration"},"description":"20 most recent, newest first."}},"required":["migrations"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Content type not found, or belongs to a different space/environment than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Start a controlled content-model migration","description":"Plans and enqueues an async migration that preserves data (BullMQ). 202 on success — the migration runs in the background; poll GET .../migrations/{migrationId}. 422 covers every \"this proposal cannot run\" case (blocked change, missing defaults, nothing to migrate, too many operations); 429 if the space already has 3 migrations running.","tags":["Content-Model Migrations (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"baseRevision":{"type":"string","format":"date-time","description":"The content type's current updatedAt, ISO-8601 UTC — the optimistic-lock check (409 if the model changed since it was loaded)."},"fields":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"The PROPOSED full field array (same shape as ContentType.fields)."},"fieldSchema":{},"defaults":{"type":"object","additionalProperties":{},"description":"Default values for new required fields, keyed by field id. At most 64 KB; validated against the same rules a record write uses."},"planHash":{"type":"string","minLength":1,"description":"Must match the hash of the plan derived from `fields`/`defaults` server-side (409 code:\"plan-mismatch\" otherwise) — the proposal executed must be exactly what was reviewed."}},"required":["baseRevision","fields","planHash"]}}}},"responses":{"202":{"description":"Migration planned and enqueued.","content":{"application/json":{"schema":{"type":"object","properties":{"migration":{"$ref":"#/components/schemas/SerializedMigration"}},"required":["migration"]}}}},"400":{"description":"Malformed body, invalid field definitions, or defaults exceed 64 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Content type not found, or belongs to a different space/environment than the URL names.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"baseRevision is stale (model changed), planHash mismatch, or a migration is already running for this content type (code: MODEL_MIGRATING_CODE).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The proposal cannot be migrated automatically (blocked/needsInput/nothing-to-migrate/too-many-operations).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"At most 3 migrations can run at once in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Migration queue unavailable; the migration row was marked failed.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"migration":{"$ref":"#/components/schemas/SerializedMigration"}},"required":["migration"]}]}}}}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-types/{id}/migrations/{migrationId}":{"get":{"summary":"Get one migration's status","description":"Scope checked in order: the migration's own contentTypeId must equal {id}, and that content type must belong to this route's environment/space — a foreign migration is a flat 404, never 403 (which would leak its existence).","tags":["Content-Model Migrations (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"migrationId","in":"path"}],"responses":{"200":{"description":"The migration, with failedItems/changedSinceCount.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SerializedMigration"},{"type":"object","properties":{"failedItems":{"type":"array","items":{"type":"object","properties":{"contentRecordId":{"type":"string"},"error":{"type":["string","null"]}},"required":["contentRecordId","error"]}},"changedSinceCount":{"type":["integer","null"]}},"required":["failedItems","changedSinceCount"]}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, doesn't belong to {id}, or {id} isn't in this space/environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/content-types/{id}/migrations/{migrationId}/rollback":{"post":{"summary":"Roll back a completed migration","description":"Creates and enqueues a new kind:\"rollback\" migration that restores the pre-migration field state and copies back the original ContentModelMigrationItem versions. 409 (code: \"not-rollbackable\") if the migration is not a completed, not-yet-rolled-back \"migrate\". Checked BEFORE the body is parsed, so a malformed body on an already-rolled-back migration still answers 409, not 400.","tags":["Content-Model Migrations (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"ContentType id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"migrationId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"includeChangedSince":{"type":"boolean","description":"Default false: records changed since the migration ran are skipped by the rollback, not reverted (see the migration's own doc comment)."}}}}}},"responses":{"202":{"description":"Rollback planned and enqueued.","content":{"application/json":{"schema":{"type":"object","properties":{"migration":{"allOf":[{"$ref":"#/components/schemas/SerializedMigration"},{"description":"The new kind:\"rollback\" migration row this call created."}]}},"required":["migration"]}}}},"400":{"description":"Malformed body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found, doesn't belong to {id}, or {id} isn't in this space/environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Not rollbackable (not completed / not kind \"migrate\" / already rolled back), or another migration is already running for this content type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Migration queue unavailable; the rollback row was marked failed.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"migration":{"$ref":"#/components/schemas/SerializedMigration"}},"required":["migration"]}]}}}}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/releases":{"get":{"summary":"List releases","description":"Each item carries `_count.items` instead of the full item array.","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","enum":["open","scheduled","executing","done","failed"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Clamped to [1, 200]. Default 50."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Matching releases.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Release"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create a release","description":"`spaceId`/`environmentId` are taken from the path, not the body.","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"}},"required":["title"]}}}},"responses":{"201":{"description":"Release created (with no items yet).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Missing required field: title.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/releases/{id}":{"get":{"summary":"Get a single release, including its items","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Release id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The release, with the full items array.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found, or belongs to a different space/environment than the URL names — a foreign release is always a flat 404 (never 403, which would leak its existence).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Update a release (rename, or schedule/unschedule it)","description":"409 if the release is no longer open/scheduled (done/executing/failed releases are immutable).","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Release id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":["string","null"]},"scheduledFor":{"type":["string","null"],"description":"ISO date, or null to cancel a schedule (resets status to \"open\"). Must be in the future; the release must have at least one item to be scheduled."}}}}}},"responses":{"200":{"description":"Release updated.","content":{"application/json":{"schema":{"type":"object","properties":{"release":{"$ref":"#/components/schemas/Release"}},"required":["release"]}}}},"400":{"description":"Nothing to update, scheduledFor not in the future/not a valid date, or scheduling an empty release.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found in this space/environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The release is no longer open/scheduled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete a release","description":"409 if the release is currently executing. Items cascade with it; already-published content is unaffected — a release is an intent, not ownership.","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Release id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Release deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found in this space/environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The release is currently executing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/releases/{id}/items":{"post":{"summary":"Add content records to a release","description":"Every record must live in the release's own environment (400 record-not-in-environment otherwise — no partial success). Duplicates are skipped, not an error. 409 if the release is no longer open/scheduled.","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Release id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recordIds":{"type":"array","items":{"type":"string"},"minItems":1},"locales":{"type":"array","items":{"type":"string"},"description":"Omitted: every known locale of each record."}},"required":["recordIds"]}}}},"responses":{"200":{"description":"Items added.","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":"integer"},"alsoIn":{"type":"array","items":{"type":"object","properties":{"recordId":{"type":"string"},"releaseId":{"type":"string"},"releaseTitle":{"type":"string"}},"required":["recordId","releaseId","releaseTitle"]},"description":"Records that were ALSO already queued in another open release — informational, not an error."}},"required":["added","alsoIn"]}}}},"400":{"description":"recordIds/locales malformed, more than MAX_RELEASE_ITEMS (200) would result, or a record isn't in the release's environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Release not found in this space/environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The release is no longer open/scheduled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Remove one item from a release","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Release id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"The ReleaseItem id (not the record id)."},"required":true,"name":"itemId","in":"query"}],"responses":{"200":{"description":"Item removed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"400":{"description":"Missing itemId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Release not found in this space/environment, or itemId doesn't belong to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/releases/{id}/execute":{"post":{"summary":"Execute a release (or dry-run it)","description":"⚠️ NOT atomic — each item is its own publish transaction; a failure partway through leaves earlier items published. 422 (not 400) when dryRun is false and pre-flight validation fails without allowPartial:true — the report is still returned so the caller can see exactly what would have failed.","tags":["Releases (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Release id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"true runs validateRelease only — writes nothing."},"allowPartial":{"type":"boolean","description":"Without it, any item failing pre-flight validation aborts the whole run (nothing is published). With it, publishable items still go out and failures are reported per item."}}}}}},"responses":{"200":{"description":"dryRun:true → {dryRun:true, report} (nothing published). dryRun false/omitted → {report} (real run; the audit row is written inside executeRelease).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"dryRun":{"type":"boolean","enum":[true]},"report":{"$ref":"#/components/schemas/ReleaseRunReport"}},"required":["dryRun","report"]},{"type":"object","properties":{"report":{"$ref":"#/components/schemas/ReleaseRunReport"}},"required":["report"]}]}}}},"400":{"description":"The release is no longer open/scheduled (\"not-executable\"), or was deleted between the authorization check and execution (\"not-found\") — {error: reason}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Release not found in this space/environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The release is already executing (race-safe claim lost).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Pre-flight validation failed and allowPartial was not set.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"report":{"$ref":"#/components/schemas/ReleaseRunReport"}},"required":["error","report"]}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/webhooks":{"get":{"summary":"List webhooks","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"This environment's webhooks.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create a webhook","description":"Never returns `secret`, only `hasSecret` — same as every other read path.","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string","description":"Validated by validateWebhookUrl (400 if invalid)."},"events":{"type":"array","items":{"type":"string"},"description":"Must all be known event types (WEBHOOK_EVENT_TYPES) — 400 otherwise. This validation is NEW for the public API; the session route never enforced it."},"active":{"type":"boolean","description":"Default: true."},"secret":{"type":["string","null"]},"format":{"type":"string","enum":["raw","slack"],"description":"Default: \"raw\"."}},"required":["name","url"]}}}},"responses":{"201":{"description":"Webhook created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Missing name/url, invalid url, unknown event type(s), or invalid format.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Space not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/webhooks/{id}":{"get":{"summary":"Get a single webhook","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Webhook id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The webhook.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found in this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Update a webhook","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Webhook id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string","description":"Validated by validateWebhookUrl."},"events":{"type":"array","items":{"type":"string"},"description":"Must all be known event types (WEBHOOK_EVENT_TYPES) — 400 otherwise."},"active":{"type":"boolean"},"secret":{"type":["string","null"],"description":"Omit to leave unchanged, null to remove."}}}}}},"responses":{"200":{"description":"Webhook updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Invalid field type, invalid url, or unknown event type(s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found in this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete a webhook","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Webhook id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string"}},"required":["deleted","id"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found in this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/webhooks/{id}/test":{"post":{"summary":"Send a synthetic test delivery","description":"Enqueues a webhook.test event; the outcome shows up under .../deliveries.","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Webhook id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Test delivery enqueued (or not, per `queued`).","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"},"url":{"type":"string"}},"required":["queued","url"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Not found in this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/webhooks/{id}/deliveries":{"get":{"summary":"List a webhook's recent delivery attempts","description":"Newest first. Retention is \"last 100 per webhook\" (trimmed by the worker on insert) — `skip` is accepted for envelope-shape consistency but has no effect beyond that cap.","tags":["Webhooks (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Webhook id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Clamped to [1, 100]. Default 100."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Recent deliveries.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Webhook not found in this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/assets":{"get":{"summary":"List assets","description":"Assets are space-scoped (no per-environment column); the :environmentId segment keeps URL parity with the sibling content-records/content-types endpoints and is still validated by withApiKey.","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Substring match against filename/title."},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","description":"Filter by Asset.contentType (mime type)."},"required":false,"name":"contentType","in":"query"},{"schema":{"type":"string","description":"Projects each item's url/filename/contentType/size/width/height to that locale's file variant when one exists (see ProjectedAsset)."},"required":false,"name":"locale","in":"query"},{"schema":{"type":"string","description":"Clamped server-side (parsePagination)."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Matching assets.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProjectedAsset"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Upload a file as an Asset","description":"Uploads to the space's blob container, derives image width/height best-effort, creates the Asset row. Requires Space.containerName to be configured (400 otherwise). The returned `url` is what content records reference. Either multipart/form-data (field `file`) or a raw body with `Content-Type: <mime>` + `X-Filename: <name>` header (+ optional `?folder=`) is accepted.","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Only used with the raw-body upload form."},"required":false,"name":"folder","in":"query"},{"schema":{"type":"string","description":"Required for the raw-body upload form; ignored for multipart."},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","description":"The file to upload.","format":"binary"},"filename":{"type":"string"},"folder":{"type":"string"}},"required":["file"]}},"application/octet-stream":{"schema":{"type":"string","description":"Raw file bytes; requires X-Filename header. Any Content-Type is accepted.","format":"binary"}}}},"responses":{"201":{"description":"Asset created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"description":"Space has no blob container configured, or the payload could not be parsed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"description":"Image exceeds the 40 megapixel limit (deriveImageDimensions, sharp's limitInputPixels).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Content-Type claims a raster image MIME type but the decoded bytes are an unsupported/mismatched format (type-confusion guard, deriveImageDimensions).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/assets/{id}":{"get":{"summary":"Get a single asset","description":"?locale= projects url/filename/contentType/size/width/height to that locale's file variant when one exists (see ProjectedAsset, same projection as the list route).","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview","management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Asset id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"locale","in":"query"}],"responses":{"200":{"description":"The asset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectedAsset"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Asset not found in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Update an asset's metadata","description":"STRICT subset of the session editor's PUT: only title/description/altText. Does not accept tags, localizedMeta or the focal point (see updateAssetMetadata's doc comment) — sending any other key 400s (schema is .strict()), it is not silently dropped.","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Asset id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":["string","null"]},"description":{"type":["string","null"]},"altText":{"type":["string","null"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Asset updated.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Asset"},{"type":"object","properties":{"files":{"type":"array","items":{"$ref":"#/components/schemas/AssetFile"}}},"required":["files"]}]}}}},"400":{"description":"Field validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Asset not found in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete an asset","description":"Refuses (409) if the asset is still referenced by any content record's active or latest version in this space; pass ?force=true to override. Deletes the blob(s) first, purges the Bunny edge cache, then removes the Asset row.","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Asset id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"],"description":"true skips the reference guard."},"required":false,"name":"force","in":"query"}],"responses":{"200":{"description":"Asset deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string"},"blobDeleted":{"type":"boolean"}},"required":["deleted","id","blobDeleted"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Asset not found in this space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Asset is still referenced by one or more content records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/assets/{id}/files/{locale}":{"post":{"summary":"Create or replace an asset's locale-variant file","description":"Replaces any existing variant for that locale (old blob deleted + purged first). Same payload parsing as the base asset-upload route (multipart or raw body + X-Filename).","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Asset id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Must exist in the space and must NOT be the space's default locale."},"required":true,"name":"locale","in":"path"},{"schema":{"type":"string","description":"Required for the raw-body upload form; ignored for multipart."},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}},"application/octet-stream":{"schema":{"type":"string","description":"Raw file bytes; requires X-Filename header. Any Content-Type is accepted.","format":"binary"}}}},"responses":{"201":{"description":"Locale-variant file created/replaced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFile"}}}},"400":{"description":"Invalid locale (unknown, or the space's default locale).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"description":"Image exceeds the 40 megapixel limit (deriveImageDimensions, sharp's limitInputPixels).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Content-Type claims a raster image MIME type but the decoded bytes are an unsupported/mismatched format (type-confusion guard, deriveImageDimensions).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete an asset's locale-variant file","tags":["Assets (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Asset id."},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Must exist in the space and must NOT be the space's default locale."},"required":true,"name":"locale","in":"path"}],"responses":{"200":{"description":"Locale-variant file deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string"}},"required":["deleted","id"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/tags":{"get":{"summary":"List tags","description":"Tags are space-scoped; the :environmentId segment only keeps URL parity with the sibling content-records/assets endpoints.","tags":["Tags (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Page size, clamped to [1, 200]. Default 100.","example":"100"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Rows to skip (offset pagination). Default 0.","example":"0"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Tags in this space, with a usage count per tag.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"summary":"Create a tag","tags":["Tags (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Required."},"slug":{"type":"string","description":"Default: slugify(name)."}},"required":["name"]}}}},"responses":{"201":{"description":"Tag created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tag"}}}},"400":{"description":"Missing name, or name/slug produced an empty slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"A tag with this name/slug already exists in the space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v2/spaces/{spaceId}/environments/{environmentId}/tags/{id}":{"get":{"summary":"Get a tag","tags":["Tags (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Tag id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The tag.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tag"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Rename a tag","description":"Name-only body keeps the existing slug.","tags":["Tags (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Tag id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"}}}}}},"responses":{"200":{"description":"Tag renamed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tag"}}}},"400":{"description":"An explicitly-sent name/slug trimmed/slugified to empty, or the body contained neither field (tag-write.ts: \"name must not be empty\" / \"slug produced an empty value\" / \"Nothing to update: provide name and/or slug\").","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"A tag with this name/slug already exists in the space.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"summary":"Delete a tag","description":"Deletes the tag and its _ContentRecordToTag join rows.","tags":["Tags (v2)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["management"],"parameters":[{"schema":{"type":"string","description":"Space id (`Space.id`)."},"required":true,"name":"spaceId","in":"path"},{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Tag id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Tag deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string"},"unlinkedRecords":{"type":"integer","description":"Number of content records that lost this tag."}},"required":["deleted","id","unlinkedRecords"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v1/health":{"get":{"summary":"Liveness check (v1)","tags":["Health"],"security":[],"x-smartcms-key-types":[],"responses":{"200":{"description":"Always 200 while the app process is up. Sent with `Cache-Control: no-store` so the CDN never answers a liveness probe from cache.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string","enum":["v1"]},"timestamp":{"type":"string","description":"ISO-8601 timestamp of the response."}},"required":["status","version","timestamp"]}}}}}}},"/content/v1/content":{"get":{"summary":"List content records (key's own environment)","description":"Only usable by an environment-scoped key (ApiKeyContext.environmentId set) — a space-wide key gets 400 with a pointer to the /environments/:environmentId/content form.","tags":["Delivery (v1)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview"],"parameters":[{"schema":{"type":"string","description":"Filter by ContentType.name."},"required":false,"name":"content_type","in":"query"},{"schema":{"type":"string","description":"Projects each item into that locale's flattened shape."},"required":false,"name":"locale","in":"query"},{"schema":{"type":"string","description":"Reference-resolution depth (only with locale set)."},"required":false,"name":"include","in":"query"},{"schema":{"type":"string","description":"Clamped to [1, 1000]. Default 10."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"},{"schema":{"type":"string","description":"field or -field; only updatedAt/createdAt are supported (v1 REST order constraint)."},"required":false,"name":"order","in":"query"}],"responses":{"200":{"description":"Matching records.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"400":{"description":"Key is not scoped to a specific environment, or an unknown query parameter was sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v1/content/{id}":{"get":{"summary":"Get a single content record","description":"?locale= triggers hydration into that locale's flattened shape; without it, the raw multi-locale shape is returned. ?include=<depth> resolves references inline (locale path only).","tags":["Delivery (v1)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"locale","in":"query"},{"schema":{"type":"string","description":"Reference-resolution depth; only with locale set."},"required":false,"name":"include","in":"query"}],"responses":{"200":{"description":"The record.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Content record not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v1/environments/{environmentId}/content":{"get":{"summary":"List content records","description":"Out-of-scope environment (not the key's own, and not in its allowedEnvironmentIds) → 404 (v1's historic contract, envParamContract:\"v1\" — distinct from v2's 403).","tags":["Delivery (v1)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview"],"parameters":[{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Filter by ContentType.name."},"required":false,"name":"content_type","in":"query"},{"schema":{"type":"string","description":"Projects each item into that locale's flattened shape."},"required":false,"name":"locale","in":"query"},{"schema":{"type":"string","description":"Reference-resolution depth (only with locale set)."},"required":false,"name":"include","in":"query"},{"schema":{"type":"string","description":"Clamped to [1, 1000]. Default 10."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"skip","in":"query"},{"schema":{"type":"string","description":"field or -field; only updatedAt/createdAt are supported (v1 REST order constraint)."},"required":false,"name":"order","in":"query"}],"responses":{"200":{"description":"Matching records.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":{},"description":"Known top-level keys (raw, multi-locale shape — the default without ?locale=): id, contentTypeId, contentType ({id,name,displayName,environmentId,fields}), data (locale-keyed field data), status, localizedStatus, availableLocales, publishedLocales, defaultLocale, version, createdAt, updatedAt, publishedAt, createdBy, updatedBy, tags ([{id,name}]), activeVersion, latestVersion. With ?locale=, the response is instead a flattened, locale-projected shape (see resolve-references.ts's projectRecordWithIncludes) — not modeled here. With ?include=, a reference/asset/richtext-embed field's resolved value is an embedded object (resolve-references.ts's resolveAssetRef for an asset) carrying {id,url,filename,contentType,size,width,height,altText,aiGenerated} plus the same sparse focalPoint/files/defaultLocale keys DeliveryAsset documents — also not modeled here."}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"400":{"description":"Unknown query parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Environment not accessible to this key (v1 404-on-env-mismatch contract).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v1/environments/{environmentId}/content-types":{"get":{"summary":"List an environment's content-type definitions","description":"Sorted by name ascending (not updatedAt) for a stable, diffable list.","tags":["Delivery (v1)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview"],"parameters":[{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Page size, clamped to [1, 1000]. Default 1000.","example":"1000"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Rows to skip (offset pagination). Default 0.","example":"0"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Content types in this environment.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryContentType"}},"total":{"type":"integer"}},"required":["items","total"]}}}},"400":{"description":"Unknown query parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Environment not accessible to this key (v1 404-on-env-mismatch contract).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v1/environments/{environmentId}/assets":{"get":{"summary":"List assets","description":"?locale= resolves url/filename/contentType/size/width/height to that locale's variant file when one exists; every item always also carries the raw `files` map and `defaultLocale`. `url` is passed through the same delivery URL optimization as asset URLs embedded in content records.","tags":["Delivery (v1)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview"],"parameters":[{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Page size, clamped to [1, 1000]. Default 1000.","example":"1000"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Rows to skip (offset pagination). Default 0.","example":"0"},"required":false,"name":"skip","in":"query"},{"schema":{"type":"string","description":"Filter by MIME type."},"required":false,"name":"content_type","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string"},"required":false,"name":"locale","in":"query"}],"responses":{"200":{"description":"Assets in this space.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryAsset"}},"total":{"type":"integer","description":"Total matching rows, independent of limit/skip."},"limit":{"type":"integer"},"skip":{"type":"integer"}},"required":["items","total","limit","skip"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Environment not accessible to this key (v1 404-on-env-mismatch contract).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/content/v1/environments/{environmentId}/tags":{"get":{"summary":"List the space's tags","description":"Tags are space-scoped; the :environmentId segment keeps URL parity with the sibling content/asset endpoints and is validated against the key's scope.","tags":["Delivery (v1)"],"security":[{"bearerAuth":[]}],"x-smartcms-key-types":["delivery","preview"],"parameters":[{"schema":{"type":"string","description":"Environment id, or the reserved alias `master` (resolved server-side to the space's default Environment before the handler runs)."},"required":true,"name":"environmentId","in":"path"},{"schema":{"type":"string","description":"Page size, clamped to [1, 1000]. Default 1000.","example":"1000"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Rows to skip (offset pagination). Default 0.","example":"0"},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Tags in this space.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["id","name","slug"]}},"total":{"type":"integer"}},"required":["items","total"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Environment not accessible to this key (v1 404-on-env-mismatch contract).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"webhooks":{}}