HTTP APIs and schema manifest
Every API is JSON over HTTP on the same listener (127.0.0.1:5080 by default). Errors are application/problem+json with type, title, status and detail. This page summarizes the contracts; the full wire contract is docs/design/contracts.md in the repository.
| Base path | Role | Authentication |
|---|---|---|
/authoring/v1 |
authoring |
Bearer token on every route except GET /config |
/delivery/v1 |
delivery |
None for public content |
/media/v1, /media/{hash}/… |
media |
Bearer token for uploads; files follow the content that uses them |
/ with Accept: application/json, /api/episerver/v3.0/… |
delivery |
As Delivery |
/ext/{name}/… |
authoring |
Bearer token; forwarded to an extension’s admin API |
/editor/ |
editor |
None (static files) |
Delivery API
Section titled “Delivery API”Read-only and cacheable. A request for public content without expansion is one SQL query.
| Method and path | Result |
|---|---|
GET /delivery/v1/route?host=&path=&expand=0|1 |
The document at that path. host defaults to the Host header. |
GET /delivery/v1/content/{id}?locale=&expand=0|1 |
The document; locale defaults to the master locale. |
GET /delivery/v1/content/{id}/children?locale= |
Published children in order |
GET /delivery/v1/preview?token=&expand=0|1 |
A preview of one version (see Security) |
GET /delivery/v1/sites |
Sites with hosts and enabled locales |
expand=1adds"expanded"to everyreferenceandcontentAreaitem, one level deep.- Every
200and304carries anETagandCache-Control: public, s-maxage=5, stale-while-revalidate=2, orprivate, no-storewhen the response includes restricted content.If-None-Matchreturns304. - A request is served from the site that lists the host, else one that lists
*, else the first site by name. - Documents outside their publishing window are
404.
The document shape is described in Publishing and projection.
Optimizely Content Delivery API v3 facade
Section titled “Optimizely Content Delivery API v3 facade”So that an existing headless front-end works without changes:
| Request | Behaviour |
|---|---|
GET /<any path> with Accept: application/json |
The page at that path in Optimizely’s shape. ?expand=* expands. |
GET /api/episerver/v3.0/content/{idOrGuid} with Accept-Language |
By GUID or by legacy integer ID |
GET /api/episerver/v3.0/content/{idOrGuid}/children |
Children |
GET /api/episerver/v3.0/site/ |
Sites with content roots, languages and hosts |
Properties are flattened onto the content, contentLink.id is the legacy integer ID when there is one, URLs are absolute on the site’s primary host, and timestamps have whole seconds, as Optimizely renders them. Known differences: workId is always 0, and rich text is sanitized.
Authoring API
Section titled “Authoring API”Base path /authoring/v1. Every handler calls the authorization interface. The main routes:
| Method and path | Purpose |
|---|---|
GET /config, GET /me |
Sign-in mode (no token needed); the caller’s name, roles and isAdmin |
GET /schema?scope=, PUT /schema?scope=&dryRun=&force= |
Read the manifest (with an ETag); replace a scope and get the plan |
GET /locales, PUT /locales/{code} |
Locales |
GET /sites, PUT /sites/{id} |
Sites, start content and hosts |
GET /settings/{key}, PUT /settings/{key} |
Settings such as globalAssetsRoot |
POST /content |
Create content with its first draft |
GET /content/{id}?locale=&version= |
The content view; version is a UUID, latest or published |
GET /content?ids=… |
Up to 200 light nodes in one request |
GET /roots, GET /content/{id}/children |
The tree |
GET /search?q=&type=&base=&under= |
Substring search over names, slugs and file names |
GET /drafts?mine= |
Work in progress |
GET /content/{id}/versions |
Version summaries |
PUT /content/{id}/draft?locale= |
Save a draft (send baseVersionId and baseRev) |
POST /content/{id}/publish?locale= |
Publish the latest version or versionId |
POST /content/{id}/schedule, /unschedule, /unpublish |
Scheduling and unpublishing |
POST /content/{id}/ready, /reject |
Review |
POST /content/{id}/move |
Move, with parentId and position |
DELETE /content/{id}, POST /content/{id}/restore, DELETE /content/{id}?purge=true, GET /trash |
Subtree delete, recycle bin, purge |
GET /content/{id}/references |
Who refers to this content |
GET /content/{id}/acl, PUT /content/{id}/acl |
Access lists |
POST /content/{id}/preview-link |
A signed preview link |
POST /content/{id}/fork, /fork/accept, /fork/detach |
Copies that remember their origin |
GET /audit |
The audit log (admins) |
PUT /sources/{id}, GET /sources, POST /sources/{id}/sync, POST /sources/{id}/adopt |
Upstream sources (overlay) |
GET /overlay, POST /content/{id}/overlay/discard, /overlay/accept-upstream |
Overlay state and actions |
POST /import/batch |
One-shot import |
POST /projection/rebuild |
Rebuild every document |
GET /jobs, POST /jobs/{name}/run, PUT /jobs/{name} |
Jobs |
GET|PUT|PATCH|DELETE /extensions/{name}, GET /ui-modules |
Extensions |
GET /tenants, GET /tenants/search, POST /mounts/use, POST /content/{id}/take-over |
Tenancy |
In the content view, data is keyed by JSON field name (mainBody); the server translates to and from field IDs. The view also carries permissions, the overlay state and schemaRevision, so a client knows when its cached schema is stale.
Problem types
Section titled “Problem types”Problems a client branches on carry a stable type:
type |
Status | When |
|---|---|---|
urn:seeamess:problem:locale-missing |
404 | The content has no version in the requested locale |
urn:seeamess:problem:stale-version |
409 | Someone saved since your baseVersionId / baseRev |
urn:seeamess:problem:validation-failed |
422 | Field values did not validate; errors lists {field, message} |
urn:seeamess:problem:breaking-schema-change |
409 | A schema push with breaking changes and without force |
urn:seeamess:problem:upstream-immutable |
409 | An upstream version would be changed here |
urn:seeamess:problem:wrong-status |
409 | The version’s status does not allow the command |
urn:seeamess:problem:delete-blocked |
409 | You lack Delete on some descendant; blockers lists them |
urn:seeamess:problem:parent-deleted |
409 | Restore without parentId while the parent is deleted |
urn:seeamess:problem:other-tenant |
409 | A command on content another tenant owns |
Media API
Section titled “Media API”| Method and path | Purpose |
|---|---|
POST /media/v1/upload?parentId=&type=&publish= |
Upload a file as new media content (X-File-Name header) |
PUT /media/v1/content/{id}/blob |
Replace the file of a media content |
POST /media/v1/blobs |
Store a blob by itself (adapters; admins) |
GET /media/{hash}/{fileName} |
The original, or a rendition with w, h, fit, format, q |
GET /globalassets/…, /siteassets/…, /contentassets/… |
Optimizely-style asset URLs |
Blobs are addressed by SHA-256, so originals and renditions are served immutable when public. Renditions without a signature are allowed only for the widths in SEEAMESS_MEDIA_WIDTHS at the default quality; anything else needs sig, signed with SEEAMESS_MEDIA_SECRET. Formats are WebP, JPEG and PNG (AVIF is not built).
Schema manifest
Section titled “Schema manifest”Produced by an SDK, accepted by PUT /authoring/v1/schema:
{ "manifestVersion": 1, "types": [ { "id": "2066d4d5-0dff-40c0-b42b-cc35e694fa73", "name": "ArticlePage", "displayName": "Article", "description": "A news or blog article.", "base": "page", "allowedChildren": [], "fields": [ { "id": "<uuid>", "name": "Summary", "displayName": "Summary", "kind": "text", "localized": true, "required": false, "group": "Content", "order": 10, "editor": "textarea", "settings": {} } ] } ]}base:page,block,media,folder.allowedChildren:nullmeans any type, an array lists type IDs.kind:text,richText,bool,int,float,dateTime,url,reference,contentArea,json,opaque.settings.allowedTypeslists type IDs forreferenceandcontentArea.- The field
nameis the C# property name (MainBody); its JSON name in delivery output starts lower case (mainBody). - A manifest is refused (
422) unless type IDs, type names, field IDs and field JSON names are unique within their scope. Unknown properties are kept.