Management API
Every /v1/cms endpoint for sites, types, folders, entries, publishing and history
Base URL: https://api.avvyr.com/v1/cms. Every request takes an Avvyr API key in x-api-key; see Keys and access. Lists answer { data, total? }, and single records answer the object. Errors are { errorCode, message, details? }.
Sites
| Method | Path | Body → answer |
|---|---|---|
| GET | /sites | { data: Site[] } |
| POST | /sites | { name, locales: [{ code, label?, default? }], previewUrl?, siteUrl? } → { site, keys: { public, preview } } |
| GET | /sites/{siteId} | Site |
| PATCH | /sites/{siteId} | { name?, locales?, previewUrl?, siteUrl?, homeEntryId? } → Site |
| DELETE | /sites/{siteId} | Deletes the site and all its content |
| POST | /sites/{siteId}/keys/{public|preview}/rotate | { key } |
| GET | /sites/{siteId}/preview-link?entryId=&locale= | { url, token, expiresAt } |
type Site = {
id: string
name: string
locales: { code: string, label: string | null, default: boolean }[]
defaultLocale: string
previewUrl: string | null // where the editor opens previews
siteUrl: string | null // the site's public address
homeEntryId: string | null // the entry served at "/"
version: number // raised on every publish
keys: { kind: 'public' | 'preview', prefix: string, createdAt: string }[]
createdAt: string
updatedAt: string
}
Types
All under /sites/{siteId}.
| Method | Path | Body → answer |
|---|---|---|
| GET | /types | { data: Type[] } |
| POST | /types | A type → Type |
| PUT | /types/{key} | The whole type → Type. 409 CMS_TYPE_MANAGED_IN_CODE for pushed types. |
| DELETE | /types/{key} | 409 CMS_TYPE_IN_USE if entries or blocks use it |
| POST | /types/push | { types: Type[], prune?, dryRun? } → { created, updated, deleted, unchanged } |
| GET | /block-examples | { data: { [blockType]: fields } }: a real block per type from the site's content |
POST /types/push is what avvyr-cms push calls. Pushed types get source: "code". See Types and fields.
Folders
| Method | Path | Body |
|---|---|---|
| GET | /sites/{siteId}/folders | All folders, flat |
| POST | /sites/{siteId}/folders | { name, parentId?, segment: { [locale]: string }, inPath? } |
| PATCH | /sites/{siteId}/folders/{folderId} | { name?, parentId?, segment?, inPath?, indexEntryId?, order? } |
| DELETE | /sites/{siteId}/folders/{folderId} | 409 CMS_FOLDER_NOT_EMPTY |
Entries
All under /sites/{siteId}.
| Method | Path | Body → answer |
|---|---|---|
| GET | /entries?folderId=&type=&q=&status=&scheduled=&limit=&offset= | { data: EntrySummary[], total } |
| POST | /entries | { typeKey, title, folderId?, slug?, locales?, fields? } → Entry |
| GET | /entries/{entryId} | Entry |
| PATCH | /entries/{entryId} | { title?, slug?, folderId?, order?, locales?, fields? } → Entry. fields replaces the whole draft. |
| DELETE | /entries/{entryId} | Unpublishes and deletes |
| POST | /entries/reorder | { folderId, ids } |
| POST | /entries/{entryId}/publish | { entry, version } |
| POST | /entries/{entryId}/unpublish | { entry, version } |
| POST | /entries/{entryId}/discard | Entry, with the draft reset to the published version |
| POST | /entries/{entryId}/preview | { locale, title?, slug?, folderId?, fields } → Document with draft: true. Saves nothing. |
| PUT | /entries/{entryId}/schedule | { publishAt?, unpublishAt? } → Entry |
| GET | /entries/{entryId}/revisions | { data: RevisionHead[] } |
| GET | /entries/{entryId}/revisions/{revisionId} | Revision |
| POST | /entries/{entryId}/revisions/{revisionId}/restore | Entry |
| POST | /publish | { entryIds } → { published, failed, version } |
| POST | /unpublish | { entryIds } → { version } |
| POST | /schedule | { entryIds, publishAt?, unpublishAt? } → { scheduled, failed } |
type EntrySummary = {
id: string
typeKey: string
title: string
folderId: string | null
order: number
slug: Record<string, string>
locales: string[]
status: 'draft' | 'published' | 'modified'
path: Record<string, string | null> // computed path per language
updatedAt: string
publishedAt: string | null
schedule: { publishAt: string | null, unpublishAt: string | null, error: object | null } | null
}
type Entry = EntrySummary & {
fields: Record<string, unknown> // the draft
publishedVersion: number | null
firstPublishedAt: string | null
}
Error codes
| Status | errorCode | When |
|---|---|---|
| 403 | ADDON_NOT_AVAILABLE | The tenant does not have the CMS add-on |
| 400 | CMS_VALIDATION | Invalid type, entry or publish; details: [{ path, message }] says what |
| 409 | CMS_PATH_CONFLICT | Another published entry already has this path |
| 409 | CMS_TYPE_EXISTS | A type with that key exists |
| 409 | CMS_TYPE_IN_USE | The type is used by entries or blocks |
| 409 | CMS_TYPE_MANAGED_IN_CODE | The type was pushed from code; change it there |
| 409 | CMS_SINGLETON_EXISTS | The singleton type already has its entry |
| 409 | CMS_ENTRY_IS_HOME | Deleting the site's home entry; set another homeEntryId first |
| 409 | CMS_ENTRY_IS_INDEX | Deleting a folder's index entry; change the folder's indexEntryId first |
| 409 | CMS_NOT_PUBLISHED | Discarding the draft of an entry that has never been published |
| 409 | CMS_FOLDER_NOT_EMPTY | The folder still has entries or folders |