Edit a Site's draft
US Region: https://public-api.sessionboard.com · EU Region: https://public-api-eu.sessionboard.com
Applies up to 60 edits, in order, to the draft. Visitors see nothing until you publish. Send the revision you last read as baseRevision: if the Site changed since, the call answers 409 with code: SITE_DRAFT_STALE and writes nothing; read it again and retry. All edits apply or none do; a block that fails its schema answers 400 naming the block. Also at PUT /v1/sites/{siteId}/draft.
Request example
curl --request PUT \ --url 'https://public-api.sessionboard.com/v1/organization/{orgId}/sites/{siteId}/draft' \ --header 'x-access-token: <api-token>' \ --header 'Content-Type: application/json' \ --data '{ "baseRevision": "1794034466000.1793990000000.1794034466000", "ops": [ { "op": "theme.set", "theme": { "mode": "dark", "accent": "#E11D48" } }, { "op": "site.update", "name": "Northwind Summit 2027" } ]}'const response = await fetch('https://public-api.sessionboard.com/v1/organization/{orgId}/sites/{siteId}/draft', { method: 'PUT', headers: { "x-access-token": "<api-token>", "Content-Type": "application/json" }, body: JSON.stringify({ "baseRevision": "1794034466000.1793990000000.1794034466000", "ops": [ { "op": "theme.set", "theme": { "mode": "dark", "accent": "#E11D48" } }, { "op": "site.update", "name": "Northwind Summit 2027" } ] }),});
const data = await response.json();console.log(data);import requests
response = requests.request( "PUT", "https://public-api.sessionboard.com/v1/organization/{orgId}/sites/{siteId}/draft", headers={ "x-access-token": "<api-token>", "Content-Type": "application/json" }, json={ "baseRevision": "1794034466000.1793990000000.1794034466000", "ops": [ { "op": "theme.set", "theme": { "mode": "dark", "accent": "#E11D48" } }, { "op": "site.update", "name": "Northwind Summit 2027" } ] },)
print(response.json())<?php
$curl = curl_init();
curl_setopt_array($curl, [ CURLOPT_URL => "https://public-api.sessionboard.com/v1/organization/{orgId}/sites/{siteId}/draft", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_POSTFIELDS => '{ "baseRevision": "1794034466000.1793990000000.1794034466000", "ops": [ { "op": "theme.set", "theme": { "mode": "dark", "accent": "#E11D48" } }, { "op": "site.update", "name": "Northwind Summit 2027" } ]}', CURLOPT_HTTPHEADER => [ "x-access-token: <api-token>", "Content-Type: application/json", ],]);
$response = curl_exec($curl);curl_close($curl);
echo $response;package main
import ( "fmt" "io" "net/http" "strings")
func main() { req, _ := http.NewRequest("PUT", "https://public-api.sessionboard.com/v1/organization/{orgId}/sites/{siteId}/draft", strings.NewReader(`{ "baseRevision": "1794034466000.1793990000000.1794034466000", "ops": [ { "op": "theme.set", "theme": { "mode": "dark", "accent": "#E11D48" } }, { "op": "site.update", "name": "Northwind Summit 2027" } ]}`)) req.Header.Add("x-access-token", "<api-token>") req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))}HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://public-api.sessionboard.com/v1/organization/{orgId}/sites/{siteId}/draft")) .header("x-access-token", "<api-token>") .header("Content-Type", "application/json") .method("PUT", HttpRequest.BodyPublishers.ofString("{\n \"baseRevision\": \"1794034466000.1793990000000.1794034466000\",\n \"ops\": [\n {\n \"op\": \"theme.set\",\n \"theme\": {\n \"mode\": \"dark\",\n \"accent\": \"#E11D48\"\n }\n },\n {\n \"op\": \"site.update\",\n \"name\": \"Northwind Summit 2027\"\n }\n ]\n}")) .build();
HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());Try it
Response headers
Authorizations
x-access-tokenstringheaderOrganization API token. Generate from Organization Settings → API Tokens.
AuthorizationBearer <token>headerorOAuth 2.1 access token. Obtained via the OAuth PKCE flow at
/oauth/token.
Send one of these. See Authentication.
Path parameters
orgIdstringpathrequiredYour organization ID. Any other organization returns
404.siteIdstring<uuid>pathrequiredThe Site's
idfrom List Sites.
Request body
baseRevisionstringThe
revisionfrom your last read (three timestamps joined by dots; send it back as is).- Max length
64
opsSiteOp[]- Max items
60
Show child attributes
opstringrequired- Allowed values
"page.add", "page.update", "page.move", "page.remove", "block.add", "block.update", "block.move", "block.remove", "nav.set", "nav.reset", "theme.set", "site.update"
brandKitIdstring<uuid> | nullThe Brand Kit the Site follows, applied when it is published.
{ "baseRevision": "1794034466000.1793990000000.1794034466000", "ops": [ { "op": "theme.set", "theme": { "mode": "dark", "accent": "#E11D48" } }, { "op": "site.update", "name": "Northwind Summit 2027" } ]}Responses
200application/json
Saved. The new revision, what changed, and the Site's pages and diff. what_changed describes this edit (before → after, in plain words); kept says how to undo it. When an API or assistant edit replaces unpublished work by someone else (or its own from more than 10 minutes ago), that draft is first kept as a version with source: checkpoint, restorable with the versions restore route.
payloadobjectShow child attributes
revisionstringchangesobject[]Show child attributes
opstringpagestringblockstring | null
siteSiteSummaryShow child attributes
idstring<uuid>typestringevent_page(Marketing),learning, orawards/event_portal/internal(Awards).productstring- Allowed values
"marketing", "learning", "awards"
namestringslugstringevent_idinteger | nullurlstring | nullThe live address.
revisionstringSend back as
baseRevisionon your next write.has_unpublished_changesbooleanpublished_atstring<date-time> | null
pagesSitePage[]Show child attributes
refstringhome, or how to refer to the page in an edit.idstring | nullnullfor a page that is new in the draft.titlestringslugstringpathstringurlstring | nullis_homebooleanhiddenbooleanvisibilitystring- Allowed values
"public", "reviewers", "submitters"
is_newbooleanblocksSiteBlock[]Show child attributes
idstringkindstringA kind from the Site's
block_catalog.hiddenbooleanconfigobjectThe block's settings, as its schema in
block_catalogdescribes.
diffSiteDiffShow child attributes
changedbooleanpagesobject[]blocksobject[]settingsobject[]Show child attributes
keystringlabelstring
summarystring[]One plain sentence per change, for the person to read.
what_changedWhatChangedWhat one edit changed, in plain words.
summaryis one sentence per change ("Changed Hero heading on the home page: “Old” → “New”"); the lists carry the same changes as data. Text is plain (HTML removed) and clipped to 200 characters; a block lists at most 8 changed fields and says how many more there were.Show child attributes
has_changesbooleansummarystring[]addedobject[]Show child attributes
wherestring | null- Example
"on the home page"
whatstring | null- Example
"FAQ"
block_idstring | nullkindstring | nulltextstring | null
changedobject[]Show child attributes
wherestring | nullwhatstring | nullblock_idstring | nullkindstring | nullfieldsobject[]Show child attributes
fieldstringlabelstringbeforestring | nullafterstring | null
more_fieldsinteger
removedobject[]Show child attributes
wherestring | nullwhatstring | nullblock_idstring | nullkindstring | nulltextstring | null
movedobject[]Show child attributes
wherestring | nullwhatstring | nullblock_idstring | nullfromintegertointeger
keptEditKeptHow to get back to what was there before this edit.
Show child attributes
keptbooleanversion_idstring | nullversioninteger | nullhowstringOne sentence saying where the earlier version is and how to restore it.
restoreobject | nullThe call that undoes this edit.
Show child attributes
toolstring- Example
"restore_site_version"
argsobject
reaches_peoplestringNothing in a draft is live; publishing needs a person.
400
An edit could not apply (the message names it), or a block failed its schema (details per block).
403
SCOPE_REQUIRED— the token lacksread:sites(orread:marketing,read:learning,read:awards) for reads, or the matchingwrite:scope for writes.FORBIDDEN— the token's user cannot edit this Site.
409
The Site changed since baseRevision (code: SITE_DRAFT_STALE).
Response examples
{ "payload": { "revision": "<string>", "changes": [ { "op": "<string>", "page": "<string>", "block": "<string>" } ], "site": { "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "type": "<string>", "product": "marketing", "name": "<string>", "slug": "<string>", "event_id": 123, "url": "<string>", "revision": "<string>", "has_unpublished_changes": true, "published_at": "2026-01-15T09:30:00Z" }, "pages": [ { "ref": "<string>", "id": "<string>", "title": "<string>", "slug": "<string>", "path": "<string>", "url": "<string>", "is_home": true, "hidden": true, "visibility": "public", "is_new": true, "blocks": [ { "id": "<string>", "kind": "<string>", "hidden": true, "config": {} } ] } ], "diff": { "changed": true, "pages": [ {} ], "blocks": [ {} ], "settings": [ { "key": "<string>", "label": "<string>" } ], "summary": [ "<string>" ] }, "what_changed": { "has_changes": true, "summary": [ "<string>" ], "added": [ { "where": "on the home page", "what": "FAQ", "block_id": "<string>", "kind": "<string>", "text": "<string>" } ], "changed": [ { "where": "<string>", "what": "<string>", "block_id": "<string>", "kind": "<string>", "fields": [ { "field": "<string>", "label": "<string>", "before": "<string>", "after": "<string>" } ], "more_fields": 123 } ], "removed": [ { "where": "<string>", "what": "<string>", "block_id": "<string>", "kind": "<string>", "text": "<string>" } ], "moved": [ { "where": "<string>", "what": "<string>", "block_id": "<string>", "from": 123, "to": 123 } ] }, "kept": { "kept": true, "version_id": "<string>", "version": 123, "how": "<string>", "restore": { "tool": "restore_site_version", "args": {} } }, "reaches_people": "<string>" }}