ReferenceReference by module

Website API

Every Website action (76) and data model (22): endpoints, scopes, input, preconditions, events and examples.

Your website: its pages and menu, design, addresses, posts, FAQs and legal pages.

Module code site · 76 actions · 22 data models · 44 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: site (everything), site:read (read only) or site:<capability>.

Resources

ResourceTableList and readPOSTPATCHDELETE
blog_authorsblog_author/api/v1/blog_authorssite.save_blog_author
blog_categoriesblog_category/api/v1/blog_categoriessite.save_blog_category
blog_post_tagsblog_post_tag/api/v1/blog_post_tags
blog_postsblog_post/api/v1/blog_posts
blog_tagsblog_tag/api/v1/blog_tagssite.save_blog_tag
client_referencesclient_reference/api/v1/client_references
contact_infoscontact_info/api/v1/contact_infos
contact_submissionscontact_submission/api/v1/contact_submissions
domainsdomain/api/v1/domainssite.add_domain
faq_categoriesfaq_category/api/v1/faq_categoriessite.save_faq_categorysite.delete_faq_category
faq_itemsfaq_item/api/v1/faq_itemssite.save_faq_itemsite.delete_faq_item
newsesnews/api/v1/newsessite.save_news
page_redirectspage_redirect/api/v1/page_redirects
pagespage/api/v1/pagessite.update_pagesite.delete_page
policiespolicy/api/v1/policies
site_code_themessite_code_theme/api/v1/site_code_themes
site_contentssite_content/api/v1/site_contents
site_deployssite_deploy/api/v1/site_deploys
site_infossite_info/api/v1/site_infos
sitessite/api/v1/sitessite.create_site
theme_snapshotstheme_snapshot/api/v1/theme_snapshots
themestheme/api/v1/themes

Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.

Actions

ActionNeedsWhat it does
site.add_domainsite:manageAdd an address
site.add_page_blocksite:writeAdd a page section
site.add_sectionsite:writeAdd a section to a page
site.assign_imagessite:writeAssign image
site.cancel_scheduled_contentsite:writeCancel scheduled publication
site.cancel_theme_schedulesite:manageCancel a scheduled theme
site.create_pagesite:writeAdd a page
site.create_sitesite:manageCreate a site
site.create_theme_draftsite:manageStart a draft
site.define_contentsite:manageDeclare the content a site uses
site.delete_blog_taxonomysite:manageDelete blog category, tag or author
site.delete_contentsite:manageRemove a content entry
site.delete_faq_categorysite:deleteRemove an FAQ group
site.delete_faq_itemsite:deleteRemove an FAQ question
site.delete_pagesite:deleteDelete a page
site.delete_redirectsite:manageRemove a redirect
site.delete_referencesite:deleteRemove a reference
site.edit_page_blocksite:writeEdit a page section
site.edit_sectionsite:writeEdit a section
site.finish_deploysite:manageRecord a deploy result
site.import_themesite:manageImport a code site’s theme
site.mark_submissionsite:writeMark a message
site.move_pagesite:writeMove a page
site.move_page_blocksite:writeMove a page section
site.move_sectionsite:writeMove a section
site.publishsite:manageTake a site live
site.publish_draftsite:managePublish the site’s changes
site.publish_postsite:writePublish a post
site.publish_themesite:managePublish a theme
site.remove_deploysite:manageTake a deployed site down
site.remove_domainsite:manageRemove an address
site.remove_page_blocksite:writeRemove a page section
site.remove_sectionsite:writeRemove a section
site.reorder_blog_categoriessite:manageReorder blog categories
site.reorder_faqsite:writeReorder the questions
site.restore_theme_snapshotsite:writeRestore a site version
site.restylesite:writeRestyle the site
site.rollback_themesite:manageRoll back to a theme
site.save_blog_authorsite:manageSave an author
site.save_blog_categorysite:manageSave a blog category
site.save_blog_tagsite:manageSave a blog tag
site.save_faq_categorysite:manageSave an FAQ group
site.save_faq_itemsite:writeSave a question
site.save_newssite:writeWrite a news item
site.save_postsite:writeWrite a post
site.save_referencesite:writeSave a reference
site.save_theme_draftsite:writeSave a theme draft
site.schedule_contentsite:writeSchedule publication
site.schedule_theme_publishsite:manageSchedule a theme
site.set_auth_modesite:manageChange visitor sign-in
site.set_chromesite:writeSet the header and footer
site.set_contact_infosite:manageSave the contact details
site.set_contentsite:manageChange site content
site.set_homepagesite:writeMake this the home page
site.set_localesite:manageSet site languages
site.set_news_statussite:writePublish or archive news
site.set_page_blockssite:writeSave a page layout
site.set_page_navsite:writeChange the menu
site.set_page_statussite:writePublish or archive a page
site.set_policysite:manageSave a legal page
site.set_primary_domainsite:manageMake an address primary
site.set_redirectsite:manageAdd a redirect
site.set_site_infosite:manageSave the company details
site.set_site_widgetssite:writeSet site widgets
site.set_theme_seosite:writeSet the search details
site.set_theme_tokenssite:writeRestyle a theme
site.share_linkssite:readShow my website
site.snapshot_themesite:manageSave a site version
site.start_deploysite:manageDeploy a site
site.submit_contactsite:writeSend a message
site.tag_postsite:writeTag a post
site.unpublishsite:manageTake a site down
site.unpublish_postsite:writeTake a post down
site.untag_postsite:writeRemove a tag
site.update_pagesite:writeEdit a page
site.verify_domainsite:manageVerify an address

site.add_domain

Add an address — Claims a hostname for a site and issues the record that proves it is yours.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/domains/actions/add_domain
API key or OAuth token · site:manage
  • REST alias: POST /api/v1/domains
  • Canonical address: POST /api/v1/actions/site.add_domain

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
hoststringyesHostname · max 253

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_hostIt must be a hostname — no scheme, path or port.
platform_domainPlatform addresses are issued with the site.
host_takenThe hostname must be free across the whole platform.

Events it publishes: site.domain_added

What it writes: domain (insert) — Registers it pending, with a verification token

curl -X POST https://app.getera.io/api/v1/domains/actions/add_domain \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","host":"Hostname"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.domain_added"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.domains.create({
  siteId: "SITE_ID",
  host: "Hostname"
})
era domains create \
  --site-id "SITE_ID" \
  --host "Hostname"
tools/call
{
  "name": "era__site__add_domain",
  "arguments": {
    "siteId": "SITE_ID",
    "host": "Hostname"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_host, platform_domain, host_taken · 429 slow down

site.add_page_block

Add a page section — Adds one renderer block to a page in the draft theme.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/add_page_block
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.add_page_block

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
pageIduuidyesPage · id of page
blockIdstringyesSection · max 120
typestringyesBlock type · max 120
dataobject / arrayyesBlock data
enabledbooleanVisible
toIndexnumberPosition · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
page_not_foundThe page must be on the theme’s site.
invalid_blocksEvery section needs a stable id, type and valid data.
duplicate_block_idTwo sections may not share an id.

Events it publishes: site.blocks_changed

What it writes: theme (update) — Adds one block to config.pageBlocks

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/add_page_block \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"pageId":"PAGE_ID","blockId":"Section","type":"Block type","data":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.addPageBlock("THEME_ID", {
  pageId: "PAGE_ID",
  blockId: "Section",
  type: "Block type",
  data: {}
})
era themes add_page_block THEME_ID --data '{"pageId":"PAGE_ID","blockId":"Section","type":"Block type","data":{}}'
tools/call
{
  "name": "era__site__add_page_block",
  "arguments": {
    "themeId": "THEME_ID",
    "pageId": "PAGE_ID",
    "blockId": "Section",
    "type": "Block type",
    "data": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, page_not_found, invalid_blocks, duplicate_block_id · 429 slow down

site.add_section

Add a section to a page — A new section on a page of the site’s draft — visitors see it after publishing.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/theme_snapshots/actions/add_section
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.add_section

Input

FieldTypeRequiredNotes
sitestringSite · Id, address or name. Empty = the site edited last. · max 200
pagestringPage · Id, address (/about), title or "home". Empty = home. · max 255
typestringyesKind of section · hero, features_grid, testimonials, faq, pricing, gallery, cta, contact_form, text, image, stats, team … · max 80
dataobject / arrayIts words and settings · { "title": "…", "subtitle": "…", "items": [ … ] }
positionnumberPosition from 1 · min 1
afterstringAfter the section · max 200
hiddenbooleanAdd it hidden

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
page_not_foundThe page must be on that site.
unknown_block_typeThe kind must be a section the site can draw.
block_not_found“After” must name a section on the page.
invalid_blocksIts data is an object.

Events it publishes: site.blocks_changed, site.theme_draft_created

What it writes: theme (update) — Changes one page of the site’s draft (a draft is forked from the live theme when there is none); theme_snapshot (insert) — Keeps an automatic checkpoint of the draft before the change; theme (insert) — Starts a draft when the site has none

curl -X POST https://app.getera.io/api/v1/theme_snapshots/actions/add_section \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"type":"Kind of section"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed",
    "site.theme_draft_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themeSnapshots.addSection({
  type: "Kind of section"
})
era theme_snapshots add_section \
  --type "Kind of section"
tools/call
{
  "name": "era__site__add_section",
  "arguments": {
    "type": "Kind of section"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, page_not_found, unknown_block_type, block_not_found, invalid_blocks · 429 slow down

site.assign_images

Assign image — Sets the same cover image on the selected content, preserving all other fields.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/blog_posts/actions/assign_images
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.assign_images

Input

FieldTypeRequiredNotes
idsobject / arrayyesSelected records
fileIduuidyesImage · id of file
contentTypestring (enum)yesContent · Allowed values: blog, news, reference

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
no_itemsChoose one to 500 records.
file_not_foundThe file must be accessible in this workspace.
image_requiredThe file must be an image.
not_foundEvery selected record must be accessible.

Events it publishes: site.post_saved, site.news_saved, site.reference_saved

What it writes: blog_post (update) — Sets the cover image without replacing content; news (update) — Sets the cover image without replacing content; client_reference (update) — Sets the cover image without replacing content

curl -X POST https://app.getera.io/api/v1/blog_posts/actions/assign_images \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"ids":{},"fileId":"FILE_ID","contentType":"blog"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.post_saved",
    "site.news_saved",
    "site.reference_saved"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogPosts.assignImages({
  ids: {},
  fileId: "FILE_ID",
  contentType: "blog"
})
era blog_posts assign_images --data '{"ids":{},"fileId":"FILE_ID","contentType":"blog"}'
tools/call
{
  "name": "era__site__assign_images",
  "arguments": {
    "ids": {},
    "fileId": "FILE_ID",
    "contentType": "blog"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_items, file_not_found, image_required, not_found · 429 slow down

site.cancel_scheduled_content

Cancel scheduled publication — Keeps the article as a draft and cancels its pending job.

Needs site:write · removes or cannot be undone

POSThttps://app.getera.io/api/v1/blog_posts/actions/cancel_scheduled_content
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.cancel_scheduled_content

Input

FieldTypeRequiredNotes
iduuidyesArticle
kindstring (enum)yesKind · Allowed values: post, news

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_scheduledThe article must have a pending schedule.

What it writes: blog_post (update) — Clears a post schedule; news (update) — Clears a news schedule

curl -X POST https://app.getera.io/api/v1/blog_posts/actions/cancel_scheduled_content \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"ID_ID","kind":"post"}'
Response
{
  "ok": true,
  "result": "…",
  "events": []
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogPosts.cancelScheduledContent({
  id: "ID_ID",
  kind: "post"
})
era blog_posts cancel_scheduled_content \
  --id "ID_ID" \
  --kind "post"
tools/call
{
  "name": "era__site__cancel_scheduled_content",
  "arguments": {
    "id": "ID_ID",
    "kind": "post"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_scheduled · 429 slow down

site.cancel_theme_schedule

Cancel a scheduled theme — Keeps the draft as a draft and cancels its pending publication.

Needs site:manage · removes or cannot be undone

POSThttps://app.getera.io/api/v1/themes/{id}/actions/cancel_theme_schedule
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.cancel_theme_schedule

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_scheduledThe theme must have a pending schedule.

Events it publishes: site.theme_scheduled

What it writes: theme (update) — Clears the schedule

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/cancel_theme_schedule \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_scheduled"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.cancelThemeSchedule("THEME_ID")
era themes cancel_theme_schedule THEME_ID
tools/call
{
  "name": "era__site__cancel_theme_schedule",
  "arguments": {
    "themeId": "THEME_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_scheduled · 429 slow down

site.create_page

Add a page — A new page on the site, as a draft and out of the menu.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/pages/{id}/actions/create_page
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.create_page

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
titlestringyesTitle · max 255
slugstringAddress · Leave empty to take it from the title. · max 255
parentIduuidFilled from the path {id} · Under · id of page
showInHeaderbooleanShow in the header menu
showInFooterbooleanShow in the footer menu
sortOrdernumberPosition · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
slug_takenThe address must be free on this site.
parent_not_foundA parent page must be on the same site.

Events it publishes: site.page_created

What it writes: page (insert) — Creates the page as a draft

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/create_page \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","title":"Title"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.page_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.createPage("PAGE_ID", {
  siteId: "SITE_ID",
  title: "Title"
})
era pages create_page PAGE_ID \
  --site-id "SITE_ID" \
  --title "Title"
tools/call
{
  "name": "era__site__create_page",
  "arguments": {
    "parentId": "PAGE_ID",
    "siteId": "SITE_ID",
    "title": "Title"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, slug_taken, parent_not_found · 429 slow down

site.create_site

Create a site — A public face for this workspace, with the platform address it answers on.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/sites/actions/create_site
API key or OAuth token · site:manage
  • REST alias: POST /api/v1/sites
  • Canonical address: POST /api/v1/actions/site.create_site

Input

FieldTypeRequiredNotes
typestring (enum)Starting layout · Only the layout the site starts from. Any site can add shop pages later. · Allowed values: business, commerce, landing, portal
namestringyesName · max 120
slugstringAddress · Leave empty to take it from the name. · max 63
defaultLocalestringDefault language · max 12
authModestring (enum)Visitor sign-in · Allowed values: guest, guest_with_login, login_required

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_slugThe name must produce a usable address.
slug_takenThis workspace already has a site at that address.
host_takenThe platform address is already answering for another site.

Events it publishes: site.created

What it writes: site (insert) — Creates the site, unpublished; domain (insert) — Issues its platform address, already verified

curl -X POST https://app.getera.io/api/v1/sites/actions/create_site \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.sites.create({
  name: "Name"
})
era sites create \
  --name "Name"
tools/call
{
  "name": "era__site__create_site",
  "arguments": {
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_slug, slug_taken, host_taken · 429 slow down

site.create_theme_draft

Start a draft — A copy of the live theme to work in, so visitors do not watch you edit.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/themes/actions/create_theme_draft
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.create_theme_draft

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
namestringName · max 120

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
draft_existsA site has one draft at a time.

Events it publishes: site.theme_draft_created

What it writes: theme (insert) — Forks the live theme into a draft

curl -X POST https://app.getera.io/api/v1/themes/actions/create_theme_draft \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_draft_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.createThemeDraft({
  siteId: "SITE_ID"
})
era themes create_theme_draft \
  --site-id "SITE_ID"
tools/call
{
  "name": "era__site__create_theme_draft",
  "arguments": {
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, draft_exists · 429 slow down

site.define_content

Declare the content a site uses — Creates or updates the editable content entries (texts, images, links, opening hours, lists) a code site binds to. Values the owner already changed are kept unless overwrite is set.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/site_contents/actions/define_content
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.define_content

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
entriesobject / arrayyesEntries · [{key:"hero.title", kind:"text", section:"hero", label:"Hero title", value:{"tr":"…","en":"…"}}] — kinds: text, rich_text, image ({fileId|url, alt}), link ({label, href}), hours ([{label, value}]), list ([{…}]), number, boolean, json.
overwritebooleanReplace values the owner changed

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_contentEvery entry needs a valid key, a known kind and a value of that kind (at most 64 KB).

Events it publishes: site.content_changed

What it writes: site_content (insert) — New entries; site_content (update) — Label, kind and section of existing entries; empty languages filled in

curl -X POST https://app.getera.io/api/v1/site_contents/actions/define_content \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","entries":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.content_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteContents.defineContent({
  siteId: "SITE_ID",
  entries: {}
})
era site_contents define_content --data '{"siteId":"SITE_ID","entries":{}}'
tools/call
{
  "name": "era__site__define_content",
  "arguments": {
    "siteId": "SITE_ID",
    "entries": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_content · 429 slow down

site.delete_blog_taxonomy

Delete blog category, tag or author — Removes unused blog vocabulary. Posts must be moved first.

Needs site:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/blog_categories/actions/delete_blog_taxonomy
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.delete_blog_taxonomy

Input

FieldTypeRequiredNotes
iduuidyesEntry
kindstring (enum)yesKind · Allowed values: category, tag, author

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
category_not_foundA category must exist.
tag_not_foundA tag must exist.
author_not_foundAn author must exist.
blog_category_in_useA category assigned to posts stays.
blog_tag_in_useA tag assigned to posts stays.
blog_author_in_useAn author assigned to posts stays.

Events it publishes: site.blog_taxonomy_changed

What it writes: blog_category (delete) — Deletes an unused category; blog_tag (delete) — Deletes an unused tag; blog_author (delete) — Deletes an unused author

curl -X POST https://app.getera.io/api/v1/blog_categories/actions/delete_blog_taxonomy \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"ID_ID","kind":"category"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blog_taxonomy_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogCategories.deleteBlogTaxonomy({
  id: "ID_ID",
  kind: "category"
})
era blog_categories delete_blog_taxonomy \
  --id "ID_ID" \
  --kind "category"
tools/call
{
  "name": "era__site__delete_blog_taxonomy",
  "arguments": {
    "id": "ID_ID",
    "kind": "category"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 category_not_found, tag_not_found, author_not_found, blog_category_in_use, blog_tag_in_use, blog_author_in_use · 429 slow down

site.delete_content

Remove a content entry — Removes an entry the site no longer uses.

Needs site:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/site_contents/actions/delete_content
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.delete_content

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
keystringyesEntry · max 100

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
content_not_foundThe entry must exist on the site.

Events it publishes: site.content_changed

What it writes: site_content (delete) — The entry

curl -X POST https://app.getera.io/api/v1/site_contents/actions/delete_content \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","key":"Entry"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.content_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteContents.deleteContent({
  siteId: "SITE_ID",
  key: "Entry"
})
era site_contents delete_content \
  --site-id "SITE_ID" \
  --key "Entry"
tools/call
{
  "name": "era__site__delete_content",
  "arguments": {
    "siteId": "SITE_ID",
    "key": "Entry"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, content_not_found · 429 slow down

site.delete_faq_category

Remove an FAQ group — Removes the group and leaves its questions ungrouped.

Needs site:delete · removes or cannot be undone

POSThttps://app.getera.io/api/v1/faq_categories/{id}/actions/delete_faq_category
API key or OAuth token · site:delete
  • REST alias: DELETE /api/v1/faq_categories/{id}
  • Canonical address: POST /api/v1/actions/site.delete_faq_category

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Group · id of faq_category

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
category_not_foundThe group must exist.

Events it publishes: site.faq_changed

What it writes: faq_category (delete) — Removes the group; faq_item (update) — Leaves its questions ungrouped through the foreign key

curl -X POST https://app.getera.io/api/v1/faq_categories/FAQ_CATEGORY_ID/actions/delete_faq_category \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.faq_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.faqCategories.deleteFaqCategory("FAQ_CATEGORY_ID")
era faq_categories delete_faq_category FAQ_CATEGORY_ID
tools/call
{
  "name": "era__site__delete_faq_category",
  "arguments": {
    "id": "FAQ_CATEGORY_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 category_not_found · 429 slow down

site.delete_faq_item

Remove an FAQ question — Removes one question from the site accordion.

Needs site:delete · removes or cannot be undone

POSThttps://app.getera.io/api/v1/faq_items/{id}/actions/delete_faq_item
API key or OAuth token · site:delete
  • REST alias: DELETE /api/v1/faq_items/{id}
  • Canonical address: POST /api/v1/actions/site.delete_faq_item

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Question · id of faq_item

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
faq_item_not_foundThe question must exist.

Events it publishes: site.faq_changed

What it writes: faq_item (delete) — Removes the question

curl -X POST https://app.getera.io/api/v1/faq_items/FAQ_ITEM_ID/actions/delete_faq_item \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.faq_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.faqItems.deleteFaqItem("FAQ_ITEM_ID")
era faq_items delete_faq_item FAQ_ITEM_ID
tools/call
{
  "name": "era__site__delete_faq_item",
  "arguments": {
    "id": "FAQ_ITEM_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 faq_item_not_found · 429 slow down

site.delete_page

Delete a page — Gone for good. Pages that came with the site can only be archived.

Needs site:delete · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/pages/{id}/actions/delete_page
API key or OAuth token · site:delete
  • REST alias: DELETE /api/v1/pages/{id}
  • Canonical address: POST /api/v1/actions/site.delete_page

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Page · id of page

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
page_not_foundThe page must exist in this workspace.
page_is_defaultA default page can only be archived.
homepage_must_stay_publishedThe home page cannot be deleted.
has_childrenA page with sub-pages cannot be deleted.

Events it publishes: site.page_deleted

What it writes: page (delete) — Removes the page

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/delete_page \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.page_deleted"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.deletePage("PAGE_ID")
era pages delete_page PAGE_ID
tools/call
{
  "name": "era__site__delete_page",
  "arguments": {
    "id": "PAGE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 page_not_found, page_is_default, homepage_must_stay_published, has_children · 429 slow down

site.delete_redirect

Remove a redirect — The old address starts returning “not found” again.

Needs site:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/page_redirects/{id}/actions/delete_redirect
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.delete_redirect

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Redirect · id of page_redirect

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
redirect_not_foundThe redirect must exist.

Events it publishes: site.redirect_changed

What it writes: page_redirect (delete) — Removes the redirect

curl -X POST https://app.getera.io/api/v1/page_redirects/PAGE_REDIRECT_ID/actions/delete_redirect \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.redirect_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pageRedirects.deleteRedirect("PAGE_REDIRECT_ID")
era page_redirects delete_redirect PAGE_REDIRECT_ID
tools/call
{
  "name": "era__site__delete_redirect",
  "arguments": {
    "id": "PAGE_REDIRECT_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 redirect_not_found · 429 slow down

site.delete_reference

Remove a reference — Removes a client project and its testimonial from the site.

Needs site:delete · removes or cannot be undone

POSThttps://app.getera.io/api/v1/client_references/{id}/actions/delete_reference
API key or OAuth token · site:delete
  • Canonical address: POST /api/v1/actions/site.delete_reference

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Reference · id of client_reference

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
reference_not_foundThe reference must exist.

Events it publishes: site.reference_deleted

What it writes: client_reference (delete) — Removes the reference

curl -X POST https://app.getera.io/api/v1/client_references/CLIENT_REFERENCE_ID/actions/delete_reference \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.reference_deleted"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.clientReferences.deleteReference("CLIENT_REFERENCE_ID")
era client_references delete_reference CLIENT_REFERENCE_ID
tools/call
{
  "name": "era__site__delete_reference",
  "arguments": {
    "id": "CLIENT_REFERENCE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 reference_not_found · 429 slow down

site.edit_page_block

Edit a page section — Replaces the data, type or visibility of one renderer block.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/edit_page_block
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.edit_page_block

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
pageIduuidyesPage · id of page
blockIdstringyesSection · max 120
typestringBlock type · max 120
dataobject / arrayBlock data
enabledbooleanVisible

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
page_not_foundThe page must be on the theme’s site.
block_not_foundThe section must be on the page.
invalid_blocksEvery section needs a stable id, type and valid data.
duplicate_block_idTwo sections may not share an id.

Events it publishes: site.blocks_changed

What it writes: theme (update) — Edits one block in config.pageBlocks

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/edit_page_block \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"pageId":"PAGE_ID","blockId":"Section"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.editPageBlock("THEME_ID", {
  pageId: "PAGE_ID",
  blockId: "Section"
})
era themes edit_page_block THEME_ID \
  --page-id "PAGE_ID" \
  --block-id "Section"
tools/call
{
  "name": "era__site__edit_page_block",
  "arguments": {
    "themeId": "THEME_ID",
    "pageId": "PAGE_ID",
    "blockId": "Section"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, page_not_found, block_not_found, invalid_blocks, duplicate_block_id · 429 slow down

site.edit_section

Edit a section — Words, an image, settings or visibility of one section, in the site’s draft.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/theme_snapshots/actions/edit_section
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.edit_section

Input

FieldTypeRequiredNotes
sitestringSite · Id, address or name. Empty = the site edited last. · max 200
pagestringPage · Id, address (/about), title or "home". Empty = home. · max 255
sectionstringyesSection · Its id, its position from 1, its kind (hero) or words from its title. · max 200
textobject / arrayWords · { "title": "…", "subtitle": "…", "items.0.title": "…" }
imagestringImage link · An uploaded, generated or stock image: https://… or /… · max 2000
imageFieldstringWhich image · Empty = the section’s main image; items.2.imageUrl for one card. · max 200
dataobject / arraySettings to merge
visiblebooleanVisible
hideOnobject / arrayHide on · ["phone"], ["tablet"], ["desktop"]
showOnobject / arrayShow again on
localestringLanguage of the words · Empty = the site’s own language. · max 12

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
page_not_foundThe page must be on that site.
block_not_foundThe section must be on the page.
invalid_pathWords go into fields of the section, as text.
invalid_imageAn image is a plain https:// or / link.
invalid_deviceA device is phone, tablet or desktop.
invalid_blocksThe section stays renderable.
nothing_to_changeSomething must change.

Events it publishes: site.blocks_changed, site.theme_draft_created

What it writes: theme (update) — Changes one page of the site’s draft (a draft is forked from the live theme when there is none); theme_snapshot (insert) — Keeps an automatic checkpoint of the draft before the change; theme (insert) — Starts a draft when the site has none

curl -X POST https://app.getera.io/api/v1/theme_snapshots/actions/edit_section \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"section":"Section"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed",
    "site.theme_draft_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themeSnapshots.editSection({
  section: "Section"
})
era theme_snapshots edit_section \
  --section "Section"
tools/call
{
  "name": "era__site__edit_section",
  "arguments": {
    "section": "Section"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, page_not_found, block_not_found, invalid_path, invalid_image, invalid_device, invalid_blocks, nothing_to_change · 429 slow down

site.finish_deploy

Record a deploy result — Machine step: marks a version live (the previous one superseded) or failed, with its log.

Needs site:manage · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/site_deploys/{id}/actions/finish_deploy
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.finish_deploy

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Deploy · id of site_deploy
okbooleanyesSucceeded
urlstringAddress · max 500
hoststringHost · max 253
scriptNamestringWorker · max 63
providerVersionIdstringEdge version · max 100
logobject / arraySteps
errorstringError · max 2000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
deploy_not_foundThe deploy must exist in this workspace.
not_uploadingOnly a deploy still uploading can be finished.

Events it publishes: site.deployed, site.deploy_failed

What it writes: site_deploy (update) — Live or failed; the previous live version becomes superseded

curl -X POST https://app.getera.io/api/v1/site_deploys/SITE_DEPLOY_ID/actions/finish_deploy \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"ok":true}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.deployed",
    "site.deploy_failed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteDeploys.finishDeploy("SITE_DEPLOY_ID", {
  ok: true
})
era site_deploys finish_deploy SITE_DEPLOY_ID \
  --ok true
tools/call
{
  "name": "era__site__finish_deploy",
  "arguments": {
    "id": "SITE_DEPLOY_ID",
    "ok": true
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 deploy_not_found, not_uploading · 429 slow down

site.import_theme

Import a code site’s theme — Registers the sections and settings a code site declares in era.theme.json, so the owner edits them in Era’s theme editor. Values the owner already changed are kept; new settings get their defaults.

Needs site:manage · removes or cannot be undone

POSThttps://app.getera.io/api/v1/site_code_themes/actions/import_theme
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.import_theme

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
manifestobject / arrayyesera.theme.json
dryRunbooleanOnly check it
prunebooleanRemove settings the theme no longer declares

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_themeThe manifest needs a name and sections with ids, labels and settings of known types.
invalid_contentEvery default must have the shape of its setting’s type.

Events it publishes: site.theme_imported, site.content_changed

What it writes: site_code_theme (upsert) — The manifest, one per site, with a version counter; site_content (upsert) — One entry per setting; existing values are kept; site_content (delete) — Settings no longer declared, only with prune

curl -X POST https://app.getera.io/api/v1/site_code_themes/actions/import_theme \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","manifest":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_imported",
    "site.content_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteCodeThemes.importTheme({
  siteId: "SITE_ID",
  manifest: {}
})
era site_code_themes import_theme --data '{"siteId":"SITE_ID","manifest":{}}'
tools/call
{
  "name": "era__site__import_theme",
  "arguments": {
    "siteId": "SITE_ID",
    "manifest": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_theme, invalid_content · 429 slow down

site.mark_submission

Mark a message — Read, replied, archived or spam — the states the product never had.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/contact_submissions/{id}/actions/mark_submission
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.mark_submission

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Message · id of contact_submission
statusstring (enum)yesState · Allowed values: new, read, replied, archived, spam

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
submission_not_foundThe message must exist in this workspace.
no_changeIt must not already be in that state.

Events it publishes: site.submission_marked

What it writes: contact_submission (update) — Changes the message state

curl -X POST https://app.getera.io/api/v1/contact_submissions/CONTACT_SUBMISSION_ID/actions/mark_submission \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"status":"new"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.submission_marked"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.contactSubmissions.markSubmission("CONTACT_SUBMISSION_ID", {
  status: "new"
})
era contact_submissions mark_submission CONTACT_SUBMISSION_ID \
  --status "new"
tools/call
{
  "name": "era__site__mark_submission",
  "arguments": {
    "id": "CONTACT_SUBMISSION_ID",
    "status": "new"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 submission_not_found, no_change · 429 slow down

site.move_page

Move a page — Puts a page under another one, refusing anything circular.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/pages/{id}/actions/move_page
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.move_page

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Page · id of page
parentIduuidUnder · id of page

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
page_not_foundThe page must exist in this workspace.
parent_not_foundThe new parent must be on the same site.
cannot_parent_to_selfA page cannot be its own parent.
would_create_cycleA page cannot move under its own descendant.

Events it publishes: site.page_moved

What it writes: page (update) — Re-parents the page through tree_move

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/move_page \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"parentId":"PAGE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.page_moved"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.movePage("PAGE_ID", {
  parentId: "PAGE_ID"
})
era pages move_page PAGE_ID \
  --parent-id "PAGE_ID"
tools/call
{
  "name": "era__site__move_page",
  "arguments": {
    "id": "PAGE_ID",
    "parentId": "PAGE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 page_not_found, parent_not_found, cannot_parent_to_self, would_create_cycle · 429 slow down

site.move_page_block

Move a page section — Moves one renderer block to a new position on its page.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/move_page_block
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.move_page_block

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
pageIduuidyesPage · id of page
blockIdstringyesSection · max 120
toIndexnumberyesPosition · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
page_not_foundThe page must be on the theme’s site.
block_not_foundThe section must be on the page.
invalid_blocksEvery section needs a stable id, type and valid data.
duplicate_block_idTwo sections may not share an id.

Events it publishes: site.blocks_changed

What it writes: theme (update) — Reorders one block in config.pageBlocks

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/move_page_block \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"pageId":"PAGE_ID","blockId":"Section","toIndex":1}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.movePageBlock("THEME_ID", {
  pageId: "PAGE_ID",
  blockId: "Section",
  toIndex: 1
})
era themes move_page_block THEME_ID \
  --page-id "PAGE_ID" \
  --block-id "Section" \
  --to-index 1
tools/call
{
  "name": "era__site__move_page_block",
  "arguments": {
    "themeId": "THEME_ID",
    "pageId": "PAGE_ID",
    "blockId": "Section",
    "toIndex": 1
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, page_not_found, block_not_found, invalid_blocks, duplicate_block_id · 429 slow down

site.move_section

Move a section — Puts a section at another place on its page, in the draft.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/theme_snapshots/actions/move_section
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.move_section

Input

FieldTypeRequiredNotes
sitestringSite · Id, address or name. Empty = the site edited last. · max 200
pagestringPage · Id, address (/about), title or "home". Empty = home. · max 255
sectionstringyesSection · Its id, its position from 1, its kind (hero) or words from its title. · max 200
positionnumberNew position from 1 · min 1
directionstring (enum)Or · Allowed values: up, down, top, bottom

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
page_not_foundThe page must be on that site.
block_not_foundThe section must be on the page.
no_positionA position or a direction is given.
nothing_to_changeIt must not already be there.

Events it publishes: site.blocks_changed, site.theme_draft_created

What it writes: theme (update) — Changes one page of the site’s draft (a draft is forked from the live theme when there is none); theme_snapshot (insert) — Keeps an automatic checkpoint of the draft before the change; theme (insert) — Starts a draft when the site has none

curl -X POST https://app.getera.io/api/v1/theme_snapshots/actions/move_section \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"section":"Section"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed",
    "site.theme_draft_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themeSnapshots.moveSection({
  section: "Section"
})
era theme_snapshots move_section \
  --section "Section"
tools/call
{
  "name": "era__site__move_section",
  "arguments": {
    "section": "Section"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, page_not_found, block_not_found, no_position, nothing_to_change · 429 slow down

site.publish

Take a site live — A flip, not a deploy — once something points at it.

Needs site:manage · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/sites/{id}/actions/publish
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.publish

Input

FieldTypeRequiredNotes
siteIduuidyesFilled from the path {id} · Site · id of site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
no_changeIt must not already be live.
no_verified_domainAt least one address must be verified.

Events it publishes: site.published

What it writes: site (update) — Marks it published

curl -X POST https://app.getera.io/api/v1/sites/SITE_ID/actions/publish \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.sites.publish("SITE_ID")
era sites publish SITE_ID
tools/call
{
  "name": "era__site__publish",
  "arguments": {
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, no_change, no_verified_domain · 429 slow down

site.publish_draft

Publish the site’s changes — Puts the site’s draft live; the theme it replaces is archived and can be rolled back.

Needs site:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/themes/actions/publish_draft
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.publish_draft

Input

FieldTypeRequiredNotes
sitestringSite · Id, address or name. Empty = the site edited last. · max 200

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
no_draftThere must be unpublished changes.

Events it publishes: site.theme_published

What it writes: theme (update) — The draft becomes live; the live theme is archived

curl -X POST https://app.getera.io/api/v1/themes/actions/publish_draft \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"site":"Site"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.publishDraft({
  site: "Site"
})
era themes publish_draft \
  --site "Site"
tools/call
{
  "name": "era__site__publish_draft",
  "arguments": {
    "site": "Site"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, no_draft · 429 slow down

site.publish_post

Publish a post — Puts it on the blog and stamps the date the source kept forgetting.

Needs site:write · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/blog_posts/{id}/actions/publish_post
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.publish_post

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Post · id of blog_post
scheduledOnlybooleanScheduled run

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
post_not_foundThe post must exist in this workspace.
no_changeIt must not already be published.
empty_contentAn empty post cannot be published.
not_scheduledAn automatic publication must be due.

Events it publishes: site.post_published

What it writes: blog_post (update) — Publishes it and writes published_at

curl -X POST https://app.getera.io/api/v1/blog_posts/BLOG_POST_ID/actions/publish_post \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"scheduledOnly":true}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.post_published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogPosts.publishPost("BLOG_POST_ID", {
  scheduledOnly: true
})
era blog_posts publish_post BLOG_POST_ID \
  --scheduled-only true
tools/call
{
  "name": "era__site__publish_post",
  "arguments": {
    "id": "BLOG_POST_ID",
    "scheduledOnly": true
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 post_not_found, no_change, empty_content, not_scheduled · 429 slow down

site.publish_theme

Publish a theme — Swaps the live theme for this draft and keeps the old one.

Needs site:manage · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/themes/{id}/actions/publish_theme
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.publish_theme

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
already_publishedIt must not already be live.
archivedAn archived theme cannot be published.

Events it publishes: site.theme_published

What it writes: theme (update) — Publishes the draft and archives the one it replaces

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/publish_theme \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.publishTheme("THEME_ID")
era themes publish_theme THEME_ID
tools/call
{
  "name": "era__site__publish_theme",
  "arguments": {
    "themeId": "THEME_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, already_published, archived · 429 slow down

site.remove_deploy

Take a deployed site down — Records that the site was removed from the edge; its address stops answering.

Needs site:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/site_deploys/actions/remove_deploy
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.remove_deploy

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_deployedThe site must have a live deploy.

Events it publishes: site.deploy_removed

What it writes: site_deploy (update) — The live version becomes removed

curl -X POST https://app.getera.io/api/v1/site_deploys/actions/remove_deploy \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.deploy_removed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteDeploys.removeDeploy({
  siteId: "SITE_ID"
})
era site_deploys remove_deploy \
  --site-id "SITE_ID"
tools/call
{
  "name": "era__site__remove_deploy",
  "arguments": {
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_deployed · 429 slow down

site.remove_domain

Remove an address — Stops the site answering on it.

Needs site:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/domains/{id}/actions/remove_domain
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.remove_domain

Input

FieldTypeRequiredNotes
domainIduuidyesFilled from the path {id} · Address · id of domain
forcebooleanRemove anyway

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
domain_not_foundThe address must be registered here.
is_primaryThe primary address cannot be removed while others exist.
last_domainRemoving the last address of a live site is confirmed once.

Events it publishes: site.domain_removed

What it writes: domain (delete) — Removes the address

curl -X POST https://app.getera.io/api/v1/domains/DOMAIN_ID/actions/remove_domain \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"force":true}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.domain_removed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.domains.removeDomain("DOMAIN_ID", {
  force: true
})
era domains remove_domain DOMAIN_ID \
  --force true
tools/call
{
  "name": "era__site__remove_domain",
  "arguments": {
    "domainId": "DOMAIN_ID",
    "force": true
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 domain_not_found, is_primary, last_domain · 429 slow down

site.remove_page_block

Remove a page section — Removes one renderer block from a page.

Needs site:write · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/themes/{id}/actions/remove_page_block
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.remove_page_block

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
pageIduuidyesPage · id of page
blockIdstringyesSection · max 120

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
page_not_foundThe page must be on the theme’s site.
block_not_foundThe section must be on the page.
invalid_blocksEvery section needs a stable id, type and valid data.
duplicate_block_idTwo sections may not share an id.

Events it publishes: site.blocks_changed

What it writes: theme (update) — Removes one block from config.pageBlocks

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/remove_page_block \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"pageId":"PAGE_ID","blockId":"Section"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.removePageBlock("THEME_ID", {
  pageId: "PAGE_ID",
  blockId: "Section"
})
era themes remove_page_block THEME_ID \
  --page-id "PAGE_ID" \
  --block-id "Section"
tools/call
{
  "name": "era__site__remove_page_block",
  "arguments": {
    "themeId": "THEME_ID",
    "pageId": "PAGE_ID",
    "blockId": "Section"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, page_not_found, block_not_found, invalid_blocks, duplicate_block_id · 429 slow down

site.remove_section

Remove a section — Takes a section off its page in the draft; the version before is kept.

Needs site:write · removes or cannot be undone

POSThttps://app.getera.io/api/v1/theme_snapshots/actions/remove_section
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.remove_section

Input

FieldTypeRequiredNotes
sitestringSite · Id, address or name. Empty = the site edited last. · max 200
pagestringPage · Id, address (/about), title or "home". Empty = home. · max 255
sectionstringyesSection · Its id, its position from 1, its kind (hero) or words from its title. · max 200

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
page_not_foundThe page must be on that site.
block_not_foundThe section must be on the page.

Events it publishes: site.blocks_changed, site.theme_draft_created

What it writes: theme (update) — Changes one page of the site’s draft (a draft is forked from the live theme when there is none); theme_snapshot (insert) — Keeps an automatic checkpoint of the draft before the change; theme (insert) — Starts a draft when the site has none

curl -X POST https://app.getera.io/api/v1/theme_snapshots/actions/remove_section \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"section":"Section"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed",
    "site.theme_draft_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themeSnapshots.removeSection({
  section: "Section"
})
era theme_snapshots remove_section \
  --section "Section"
tools/call
{
  "name": "era__site__remove_section",
  "arguments": {
    "section": "Section"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, page_not_found, block_not_found · 429 slow down

site.reorder_blog_categories

Reorder blog categories — Sets the category order used on the site.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/blog_categories/actions/reorder_blog_categories
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.reorder_blog_categories

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
idsobject / arrayyesCategories in order

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_blog_category_orderEvery category appears once.

Events it publishes: site.blog_taxonomy_changed

What it writes: blog_category (update) — Rewrites category positions

curl -X POST https://app.getera.io/api/v1/blog_categories/actions/reorder_blog_categories \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","ids":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blog_taxonomy_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogCategories.reorderBlogCategories({
  siteId: "SITE_ID",
  ids: {}
})
era blog_categories reorder_blog_categories --data '{"siteId":"SITE_ID","ids":{}}'
tools/call
{
  "name": "era__site__reorder_blog_categories",
  "arguments": {
    "siteId": "SITE_ID",
    "ids": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_blog_category_order · 429 slow down

site.reorder_faq

Reorder the questions — The order they appear in on the site.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/faq_items/actions/reorder_faq
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.reorder_faq

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
idsobject / arrayyesNew order

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_orderThe new order must be a list of question ids.
unknown_faq_itemEvery id must be a question on this site.

Events it publishes: site.faq_changed

What it writes: faq_item (update) — Rewrites the positions

curl -X POST https://app.getera.io/api/v1/faq_items/actions/reorder_faq \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","ids":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.faq_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.faqItems.reorderFaq({
  siteId: "SITE_ID",
  ids: {}
})
era faq_items reorder_faq --data '{"siteId":"SITE_ID","ids":{}}'
tools/call
{
  "name": "era__site__reorder_faq",
  "arguments": {
    "siteId": "SITE_ID",
    "ids": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_order, unknown_faq_item · 429 slow down

site.restore_theme_snapshot

Restore a site version — Puts a saved version back into the site draft; the live site is untouched.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/actions/restore_theme_snapshot
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.restore_theme_snapshot

Input

FieldTypeRequiredNotes
snapshotIduuidyesVersion

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
snapshot_not_foundThe saved version must exist in this workspace.
no_draftThe site must have a draft to restore into.

Events it publishes: site.theme_changed

What it writes: theme (update) — Replaces the draft configuration with the saved one

curl -X POST https://app.getera.io/api/v1/themes/actions/restore_theme_snapshot \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"snapshotId":"SNAPSHOT_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.restoreThemeSnapshot({
  snapshotId: "SNAPSHOT_ID"
})
era themes restore_theme_snapshot \
  --snapshot-id "SNAPSHOT_ID"
tools/call
{
  "name": "era__site__restore_theme_snapshot",
  "arguments": {
    "snapshotId": "SNAPSHOT_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 snapshot_not_found, no_draft · 429 slow down

site.restyle

Restyle the site — Brand colour, colours, fonts, corners, spacing and dark mode of the site’s draft.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/actions/restyle
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.restyle

Input

FieldTypeRequiredNotes
sitestringSite · Id, address or name. Empty = the site edited last. · max 200
brandColorstringBrand colour · #1f6feb — buttons, links and accents follow it. · max 9
colorsobject / arrayColours · Any of background, foreground, primary, primaryForeground, secondary, secondaryForeground, accent, accentForeground, muted, mutedForeground, border, input, ring, card, cardForeground, destructive, destructiveForeground as #rrggbb.
darkColorsobject / arrayDark-mode colours · The same tokens, for the dark scheme.
headingFontstringHeading font · inter, geist, system, serif, mono, playfair, poppins, montserrat, dm-serif, roboto, fraunces, manrope · max 40
bodyFontstringText font · max 40
baseSizenumberText size (px)
radiusnumberCorner roundness (0–2)
spacingstring (enum)Spacing · Allowed values: compact, default, relaxed
containerWidthstring (enum)Content width · Allowed values: narrow, default, wide, full
buttonStylestring (enum)Buttons · Allowed values: default, pill, square
darkbooleanDark mode

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_colorColours are known theme colours, as #rrggbb.
unknown_fontA font must be one the site loads.
invalid_tokensSizes and choices are in range.
nothing_to_changeSomething must change.

Events it publishes: site.theme_changed, site.theme_draft_created

What it writes: theme (update) — Writes the colour, type and layout tokens of the draft; theme (insert) — Starts a draft when the site has none; theme_snapshot (insert) — Keeps an automatic checkpoint first

curl -X POST https://app.getera.io/api/v1/themes/actions/restyle \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"site":"Site"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed",
    "site.theme_draft_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.restyle({
  site: "Site"
})
era themes restyle \
  --site "Site"
tools/call
{
  "name": "era__site__restyle",
  "arguments": {
    "site": "Site"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_color, unknown_font, invalid_tokens, nothing_to_change · 429 slow down

site.rollback_theme

Roll back to a theme — Puts an earlier theme back on the site, and archives the one it replaces.

Needs site:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/themes/{id}/actions/rollback_theme
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.rollback_theme

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
already_publishedIt must not already be live.
not_archivedRolling back goes to a theme that was live before.

Events it publishes: site.theme_published

What it writes: theme (update) — Swaps the live theme for the archived one

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/rollback_theme \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.rollbackTheme("THEME_ID")
era themes rollback_theme THEME_ID
tools/call
{
  "name": "era__site__rollback_theme",
  "arguments": {
    "themeId": "THEME_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, already_published, not_archived · 429 slow down

site.save_blog_author

Save an author — How somebody in the team is introduced on this site.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/blog_authors/{id}/actions/save_blog_author
API key or OAuth token · site:manage
  • REST alias: PATCH /api/v1/blog_authors/{id}
  • Canonical address: POST /api/v1/actions/site.save_blog_author

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Author · id of blog_author
userIduuidyesPerson · id of app_user
displayNamestringyesShown as · max 120
biostringIntroduction · max 4000
isPublicbooleanVisible on the site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
user_not_foundThe person must be in this organization.
author_existsOne author entry per person per site.
author_not_foundAn edit must name an author that exists.

Events it publishes: site.blog_taxonomy_changed

What it writes: blog_author (upsert) — Writes the author

curl -X POST https://app.getera.io/api/v1/blog_authors/BLOG_AUTHOR_ID/actions/save_blog_author \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","userId":"APP_USER_ID","displayName":"Shown as"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blog_taxonomy_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogAuthors.saveBlogAuthor("BLOG_AUTHOR_ID", {
  siteId: "SITE_ID",
  userId: "APP_USER_ID",
  displayName: "Shown as"
})
era blog_authors save_blog_author BLOG_AUTHOR_ID \
  --site-id "SITE_ID" \
  --user-id "APP_USER_ID" \
  --display-name "Shown as"
tools/call
{
  "name": "era__site__save_blog_author",
  "arguments": {
    "id": "BLOG_AUTHOR_ID",
    "siteId": "SITE_ID",
    "userId": "APP_USER_ID",
    "displayName": "Shown as"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, user_not_found, author_exists, author_not_found · 429 slow down

site.save_blog_category

Save a blog category — The groups posts are filed under.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/blog_categories/{id}/actions/save_blog_category
API key or OAuth token · site:manage
  • REST alias: PATCH /api/v1/blog_categories/{id}
  • Canonical address: POST /api/v1/actions/site.save_blog_category

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Category · id of blog_category
namestringyesName · max 100
slugstringAddress · max 100
descriptionstringDescription · max 2000
colorstringColour · max 7
iconstringIcon · max 50
sortOrdernumberPosition · min 0
isActivebooleanIn use

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_slugThe name must produce a usable address.
slug_takenThe address must be free on this site.
invalid_colourA colour must be a six-digit hex value.
category_not_foundAn edit must name a category that exists.

Events it publishes: site.blog_taxonomy_changed

What it writes: blog_category (upsert) — Writes the category

curl -X POST https://app.getera.io/api/v1/blog_categories/BLOG_CATEGORY_ID/actions/save_blog_category \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blog_taxonomy_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogCategories.saveBlogCategory("BLOG_CATEGORY_ID", {
  siteId: "SITE_ID",
  name: "Name"
})
era blog_categories save_blog_category BLOG_CATEGORY_ID \
  --site-id "SITE_ID" \
  --name "Name"
tools/call
{
  "name": "era__site__save_blog_category",
  "arguments": {
    "id": "BLOG_CATEGORY_ID",
    "siteId": "SITE_ID",
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_slug, slug_taken, invalid_colour, category_not_found · 429 slow down

site.save_blog_tag

Save a blog tag — A label posts can carry.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/blog_tags/{id}/actions/save_blog_tag
API key or OAuth token · site:manage
  • REST alias: PATCH /api/v1/blog_tags/{id}
  • Canonical address: POST /api/v1/actions/site.save_blog_tag

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Tag · id of blog_tag
namestringyesName · max 50
slugstringAddress · max 50
isPublicbooleanVisible on the site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_slugThe name must produce a usable address.
slug_takenThe address must be free on this site.
tag_not_foundAn edit must name a tag that exists.

Events it publishes: site.blog_taxonomy_changed

What it writes: blog_tag (upsert) — Writes the tag

curl -X POST https://app.getera.io/api/v1/blog_tags/BLOG_TAG_ID/actions/save_blog_tag \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blog_taxonomy_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogTags.saveBlogTag("BLOG_TAG_ID", {
  siteId: "SITE_ID",
  name: "Name"
})
era blog_tags save_blog_tag BLOG_TAG_ID \
  --site-id "SITE_ID" \
  --name "Name"
tools/call
{
  "name": "era__site__save_blog_tag",
  "arguments": {
    "id": "BLOG_TAG_ID",
    "siteId": "SITE_ID",
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_slug, slug_taken, tag_not_found · 429 slow down

site.save_faq_category

Save an FAQ group — The headings the questions sit under.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/faq_categories/{id}/actions/save_faq_category
API key or OAuth token · site:manage
  • REST alias: PATCH /api/v1/faq_categories/{id}
  • Canonical address: POST /api/v1/actions/site.save_faq_category

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Group · id of faq_category
namestringyesName · max 100
slugstringAddress · max 100
descriptionstringDescription · max 2000
iconstringIcon · max 50
sortOrdernumberPosition · min 0
isActivebooleanShown on the site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_slugThe name must produce a usable address.
slug_takenThe address must be free on this site.
category_not_foundAn edit must name a group that exists.

Events it publishes: site.faq_changed

What it writes: faq_category (upsert) — Writes the group

curl -X POST https://app.getera.io/api/v1/faq_categories/FAQ_CATEGORY_ID/actions/save_faq_category \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.faq_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.faqCategories.saveFaqCategory("FAQ_CATEGORY_ID", {
  siteId: "SITE_ID",
  name: "Name"
})
era faq_categories save_faq_category FAQ_CATEGORY_ID \
  --site-id "SITE_ID" \
  --name "Name"
tools/call
{
  "name": "era__site__save_faq_category",
  "arguments": {
    "id": "FAQ_CATEGORY_ID",
    "siteId": "SITE_ID",
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_slug, slug_taken, category_not_found · 429 slow down

site.save_faq_item

Save a question — One question and its answer.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/faq_items/{id}/actions/save_faq_item
API key or OAuth token · site:write
  • REST alias: PATCH /api/v1/faq_items/{id}
  • Canonical address: POST /api/v1/actions/site.save_faq_item

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Question · id of faq_item
categoryIduuidGroup · id of faq_category
questionstringyesQuestion · max 1000
answerstringyesAnswer · max 20000
sortOrdernumberPosition · min 0
isActivebooleanShown on the site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
category_not_foundA group must be on the same site.
faq_item_not_foundAn edit must name a question that exists.

Events it publishes: site.faq_changed

What it writes: faq_item (upsert) — Writes the question

curl -X POST https://app.getera.io/api/v1/faq_items/FAQ_ITEM_ID/actions/save_faq_item \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","question":"Question","answer":"Answer"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.faq_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.faqItems.saveFaqItem("FAQ_ITEM_ID", {
  siteId: "SITE_ID",
  question: "Question",
  answer: "Answer"
})
era faq_items save_faq_item FAQ_ITEM_ID \
  --site-id "SITE_ID" \
  --question "Question" \
  --answer "Answer"
tools/call
{
  "name": "era__site__save_faq_item",
  "arguments": {
    "id": "FAQ_ITEM_ID",
    "siteId": "SITE_ID",
    "question": "Question",
    "answer": "Answer"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, category_not_found, faq_item_not_found · 429 slow down

site.save_news

Write a news item — Creates or edits an announcement. Publishing is a separate step.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/newses/{id}/actions/save_news
API key or OAuth token · site:write
  • REST alias: PATCH /api/v1/newses/{id}
  • Canonical address: POST /api/v1/actions/site.save_news

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · News item · id of news
titlestringyesTitle · max 255
slugstringAddress · max 255
descriptionstringSummary · max 2000
contentobject / arrayBody
metaTitlestringSearch title · max 255
metaDescriptionstringSearch description · max 2000
isFeaturedbooleanFeatured
featuredImageIduuidCover image · id of file
publishedAtdatePublication date

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_slugThe title must produce a usable address.
slug_takenThe address must be free on this site.
news_not_foundAn edit must name an item that exists.

Events it publishes: site.news_saved

What it writes: news (upsert) — Writes the news item

curl -X POST https://app.getera.io/api/v1/newses/NEWSE_ID/actions/save_news \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","title":"Title"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.news_saved"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.newses.saveNews("NEWSE_ID", {
  siteId: "SITE_ID",
  title: "Title"
})
era newses save_news NEWSE_ID \
  --site-id "SITE_ID" \
  --title "Title"
tools/call
{
  "name": "era__site__save_news",
  "arguments": {
    "id": "NEWSE_ID",
    "siteId": "SITE_ID",
    "title": "Title"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_slug, slug_taken, news_not_found · 429 slow down

site.save_post

Write a post — Creates or edits a blog post. Publishing is a separate step.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/blog_posts/{id}/actions/save_post
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.save_post

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Post · id of blog_post
titlestringyesTitle · max 255
slugstringAddress · max 255
excerptstringExcerpt · max 2000
contentobject / arrayBody
categoryIduuidCategory · id of blog_category
authorIduuidAuthor · id of blog_author
metaTitlestringSearch title · max 255
metaDescriptionstringSearch description · max 2000
metaKeywordsstringKeywords · max 500
isFeaturedbooleanFeatured
featuredImageIduuidCover image · id of file

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_slugThe title must produce a usable address.
slug_takenThe address must be free on this site.
category_not_foundA category must be on the same site.
author_not_foundAn author must be on the same site.
post_not_foundAn edit must name a post that exists.

Events it publishes: site.post_saved

What it writes: blog_post (upsert) — Writes the post as a draft

curl -X POST https://app.getera.io/api/v1/blog_posts/BLOG_POST_ID/actions/save_post \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","title":"Title"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.post_saved"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogPosts.savePost("BLOG_POST_ID", {
  siteId: "SITE_ID",
  title: "Title"
})
era blog_posts save_post BLOG_POST_ID \
  --site-id "SITE_ID" \
  --title "Title"
tools/call
{
  "name": "era__site__save_post",
  "arguments": {
    "id": "BLOG_POST_ID",
    "siteId": "SITE_ID",
    "title": "Title"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_slug, slug_taken, category_not_found, author_not_found, post_not_found · 429 slow down

site.save_reference

Save a reference — A project you did and what the client said about it.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/client_references/{id}/actions/save_reference
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.save_reference

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
iduuidFilled from the path {id} · Reference · id of client_reference
clientNamestringyesClient · max 255
clientSectorstringSector · max 100
clientLogoIduuidLogo · id of file
projectTitlestringProject · max 255
projectDescriptionstringWhat you did · max 8000
projectImagesobject / arrayProject images
projectUrlstringLink · max 500
testimonialstringQuote · max 4000
testimonialAuthorstringSaid by · max 255
testimonialPositionstringTheir role · max 100
isFeaturedbooleanFeatured
isPublicbooleanShown on the site
sortOrdernumberPosition · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
client_name_requiredA reference needs a client name.
reference_not_foundAn edit must name a reference that exists.
invalid_reference_imagesProject images must be a list of file ids.
reference_file_not_foundEvery image must be a file in this workspace.

Events it publishes: site.reference_saved

What it writes: client_reference (upsert) — Writes the reference

curl -X POST https://app.getera.io/api/v1/client_references/CLIENT_REFERENCE_ID/actions/save_reference \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","clientName":"Client"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.reference_saved"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.clientReferences.saveReference("CLIENT_REFERENCE_ID", {
  siteId: "SITE_ID",
  clientName: "Client"
})
era client_references save_reference CLIENT_REFERENCE_ID \
  --site-id "SITE_ID" \
  --client-name "Client"
tools/call
{
  "name": "era__site__save_reference",
  "arguments": {
    "id": "CLIENT_REFERENCE_ID",
    "siteId": "SITE_ID",
    "clientName": "Client"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, client_name_required, reference_not_found, invalid_reference_images, reference_file_not_found · 429 slow down

site.save_theme_draft

Save a theme draft — Atomically saves the complete editor state into a draft, never the live theme.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/save_theme_draft
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.save_theme_draft

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
configobject / arrayyesTheme configuration

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
invalid_themeThe editor sends one versioned theme configuration.
missing_colour_tokenA palette carries all seventeen tokens.
unknown_fontA font must be one the runtime loads.
invalid_tokensTheme tokens and sizes match the runtime contract.
invalid_chromeHeader and footer blocks and positions have valid shapes.
unknown_variantLegacy chrome uses a layout the renderer draws.
page_not_foundEvery page layout belongs to this site.
invalid_blocksEvery page body is a list of renderable blocks.
duplicate_block_idTwo blocks on a page may not share an id.
invalid_seoSEO sections are objects keyed by page.
meta_title_too_longA meta title is at most 255 characters.

Events it publishes: site.theme_changed

What it writes: theme (update) — Replaces one draft config in a single transaction

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/save_theme_draft \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"config":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.saveThemeDraft("THEME_ID", {
  config: {}
})
era themes save_theme_draft THEME_ID --data '{"config":{}}'
tools/call
{
  "name": "era__site__save_theme_draft",
  "arguments": {
    "themeId": "THEME_ID",
    "config": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, invalid_theme, missing_colour_token, unknown_font, invalid_tokens, invalid_chrome, unknown_variant, page_not_found, invalid_blocks, duplicate_block_id, invalid_seo, meta_title_too_long · 429 slow down

site.schedule_content

Schedule publication — Queues a blog post or news item to go live at a future time.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/blog_posts/actions/schedule_content
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.schedule_content

Input

FieldTypeRequiredNotes
iduuidyesArticle
kindstring (enum)yesKind · Allowed values: post, news
publishAtdate-timeyesPublish at

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
post_not_foundA blog post must exist.
news_not_foundA news item must exist.
empty_contentAn article needs content.
publish_time_in_pastThe publication time must be in the future.

What it writes: blog_post (update) — Stores a post schedule; news (update) — Stores a news schedule

curl -X POST https://app.getera.io/api/v1/blog_posts/actions/schedule_content \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"ID_ID","kind":"post","publishAt":"2026-11-02T09:00:00Z"}'
Response
{
  "ok": true,
  "result": "…",
  "events": []
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogPosts.scheduleContent({
  id: "ID_ID",
  kind: "post",
  publishAt: "2026-11-02T09:00:00Z"
})
era blog_posts schedule_content \
  --id "ID_ID" \
  --kind "post" \
  --publish-at "2026-11-02T09:00:00Z"
tools/call
{
  "name": "era__site__schedule_content",
  "arguments": {
    "id": "ID_ID",
    "kind": "post",
    "publishAt": "2026-11-02T09:00:00Z"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 post_not_found, news_not_found, empty_content, publish_time_in_past · 429 slow down

site.schedule_theme_publish

Schedule a theme — Publishes the draft automatically at the chosen time.

Needs site:manage · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/themes/{id}/actions/schedule_theme_publish
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.schedule_theme_publish

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
publishAtdate-timeyesPublish at

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
not_a_draftOnly a draft can be scheduled.
publish_time_in_pastThe time must be in the future.

Events it publishes: site.theme_scheduled

What it writes: theme (update) — Stores when the draft goes live

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/schedule_theme_publish \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"publishAt":"2026-11-02T09:00:00Z"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_scheduled"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.scheduleThemePublish("THEME_ID", {
  publishAt: "2026-11-02T09:00:00Z"
})
era themes schedule_theme_publish THEME_ID \
  --publish-at "2026-11-02T09:00:00Z"
tools/call
{
  "name": "era__site__schedule_theme_publish",
  "arguments": {
    "themeId": "THEME_ID",
    "publishAt": "2026-11-02T09:00:00Z"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, not_a_draft, publish_time_in_past · 429 slow down

site.set_auth_mode

Change visitor sign-in — Controls whether this site accepts guests, sign-ins, or signed-in visitors only.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/sites/{id}/actions/set_auth_mode
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_auth_mode

Input

FieldTypeRequiredNotes
siteIduuidyesFilled from the path {id} · Site · id of site
authModestring (enum)yesVisitor sign-in · Allowed values: guest, guest_with_login, login_required

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.

Events it publishes: site.auth_mode_changed

What it writes: site (update) — Changes the visitor authentication mode

curl -X POST https://app.getera.io/api/v1/sites/SITE_ID/actions/set_auth_mode \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"authMode":"guest"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.auth_mode_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.sites.setAuthMode("SITE_ID", {
  authMode: "guest"
})
era sites set_auth_mode SITE_ID \
  --auth-mode "guest"
tools/call
{
  "name": "era__site__set_auth_mode",
  "arguments": {
    "siteId": "SITE_ID",
    "authMode": "guest"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found · 429 slow down

site.set_chrome

Set the header and footer — Which of the eight header and seven footer layouts the site uses.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/set_chrome
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_chrome

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
headerobject / arrayHeader
footerobject / arrayFooter
headerPositionnumberHeader sits at · min 0
footerPositionnumberFooter sits at · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
invalid_chromeHeader and footer are objects; positions are indexes.
unknown_variantThe layout must be one the renderer draws.

Events it publishes: site.theme_changed

What it writes: theme (update) — Writes config.chrome and the legacy header/footer keys

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/set_chrome \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"header":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.setChrome("THEME_ID", {
  header: {}
})
era themes set_chrome THEME_ID --data '{"header":{}}'
tools/call
{
  "name": "era__site__set_chrome",
  "arguments": {
    "themeId": "THEME_ID",
    "header": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, invalid_chrome, unknown_variant · 429 slow down

site.set_contact_info

Save the contact details — The address, the phone number and the six social links the footer draws.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/contact_infos/actions/set_contact_info
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_contact_info

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
emailemailEmail · max 255
phonephonePhone · max 50
addressstringAddress · max 1000
instagramUrlstringInstagram · max 500
tiktokUrlstringTikTok · max 500
linkedinUrlstringLinkedIn · max 500
facebookUrlstringFacebook · max 500
youtubeUrlstringYouTube · max 500
twitterUrlstringX · max 500

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_urlEvery social link must be a web address.

Events it publishes: site.contact_info_changed

What it writes: contact_info (upsert) — Writes the one row this site has

curl -X POST https://app.getera.io/api/v1/contact_infos/actions/set_contact_info \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.contact_info_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.contactInfos.setContactInfo({
  siteId: "SITE_ID"
})
era contact_infos set_contact_info \
  --site-id "SITE_ID"
tools/call
{
  "name": "era__site__set_contact_info",
  "arguments": {
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_url · 429 slow down

site.set_content

Change site content — Saves one content entry of a site: the whole value, or one language of it. The site shows it on its next visit.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/site_contents/actions/set_content
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_content

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
keystringyesEntry · max 100
valueobject / arrayyesValue
localestringLanguage · Only this language (tr, en …); leave empty to send every language. · max 5
labelstringName · max 200
isPublicbooleanShown on the site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
content_not_foundThe entry must exist on the site (declare it with site.define_content).
invalid_contentThe value must have the shape of the entry’s kind.

Events it publishes: site.content_changed

What it writes: site_content (update) — The entry’s value

curl -X POST https://app.getera.io/api/v1/site_contents/actions/set_content \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","key":"Entry","value":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.content_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteContents.setContent({
  siteId: "SITE_ID",
  key: "Entry",
  value: {}
})
era site_contents set_content --data '{"siteId":"SITE_ID","key":"Entry","value":{}}'
tools/call
{
  "name": "era__site__set_content",
  "arguments": {
    "siteId": "SITE_ID",
    "key": "Entry",
    "value": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, content_not_found, invalid_content · 429 slow down

site.set_homepage

Make this the home page — What a visitor sees at the root of the site.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/pages/{id}/actions/set_homepage
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_homepage

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Page · id of page

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
page_not_foundThe page must exist in this workspace.
no_changeIt must not already be the home page.
page_not_publishedOnly a published page can be the home page.

Events it publishes: site.homepage_changed

What it writes: page (update) — Moves the home-page flag, in one transaction

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/set_homepage \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.homepage_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.setHomepage("PAGE_ID")
era pages set_homepage PAGE_ID
tools/call
{
  "name": "era__site__set_homepage",
  "arguments": {
    "id": "PAGE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 page_not_found, no_change, page_not_published · 429 slow down

site.set_locale

Set site languages — Chooses the language at the bare site address and the languages visitors may switch to.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/sites/{id}/actions/set_locale
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_locale

Input

FieldTypeRequiredNotes
siteIduuidyesFilled from the path {id} · Site · id of site
defaultLocalestringyesDefault language · max 16
localesobject / arrayAvailable languages

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_localeThe default must be a valid language code.

Events it publishes: site.locale_changed

What it writes: site (update) — Changes the site default and available languages

curl -X POST https://app.getera.io/api/v1/sites/SITE_ID/actions/set_locale \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"defaultLocale":"Default language"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.locale_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.sites.setLocale("SITE_ID", {
  defaultLocale: "Default language"
})
era sites set_locale SITE_ID \
  --default-locale "Default language"
tools/call
{
  "name": "era__site__set_locale",
  "arguments": {
    "siteId": "SITE_ID",
    "defaultLocale": "Default language"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_locale · 429 slow down

site.set_news_status

Publish or archive news — Draft, live, or archived.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/newses/{id}/actions/set_news_status
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_news_status

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · News item · id of news
statusstring (enum)yesState · Allowed values: draft, published, archived
scheduledOnlybooleanScheduled run

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
news_not_foundThe item must exist in this workspace.
no_changeIt must not already be in that state.
empty_contentAn empty item cannot be published.
not_scheduledAn automatic publication must be due.

Events it publishes: site.news_published

What it writes: news (update) — Changes the state and stamps published_at

curl -X POST https://app.getera.io/api/v1/newses/NEWSE_ID/actions/set_news_status \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"status":"draft"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.news_published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.newses.setNewsStatus("NEWSE_ID", {
  status: "draft"
})
era newses set_news_status NEWSE_ID \
  --status "draft"
tools/call
{
  "name": "era__site__set_news_status",
  "arguments": {
    "id": "NEWSE_ID",
    "status": "draft"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 news_not_found, no_change, empty_content, not_scheduled · 429 slow down

site.set_page_blocks

Save a page layout — The blocks that make up one page, into the draft theme only.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/set_page_blocks
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_page_blocks

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
pageIduuidyesPage · id of page
blocksobject / arrayyesBlocks

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
page_not_foundThe page must be on the theme’s site.
invalid_blocksEvery block needs an id, a type and a boolean enabled.
duplicate_block_idTwo blocks may not share an id.

Events it publishes: site.blocks_changed

What it writes: theme (update) — Writes config.pageBlocks for one page and prunes dead keys

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/set_page_blocks \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"pageId":"PAGE_ID","blocks":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.blocks_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.setPageBlocks("THEME_ID", {
  pageId: "PAGE_ID",
  blocks: {}
})
era themes set_page_blocks THEME_ID --data '{"pageId":"PAGE_ID","blocks":{}}'
tools/call
{
  "name": "era__site__set_page_blocks",
  "arguments": {
    "themeId": "THEME_ID",
    "pageId": "PAGE_ID",
    "blocks": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, page_not_found, invalid_blocks, duplicate_block_id · 429 slow down

site.set_page_nav

Change the menu — Whether a page appears in the header, the footer, and in what order.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/pages/{id}/actions/set_page_nav
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_page_nav

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Page · id of page
showInHeaderbooleanHeader menu
showInFooterbooleanFooter menu
sortOrdernumberPosition · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
page_not_foundThe page must exist in this workspace.

Events it publishes: site.navigation_changed

What it writes: page (update) — Writes the two menu flags and the position

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/set_page_nav \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"showInHeader":true}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.navigation_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.setPageNav("PAGE_ID", {
  showInHeader: true
})
era pages set_page_nav PAGE_ID \
  --show-in-header true
tools/call
{
  "name": "era__site__set_page_nav",
  "arguments": {
    "id": "PAGE_ID",
    "showInHeader": true
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 page_not_found · 429 slow down

site.set_page_status

Publish or archive a page — Draft, live, or archived — the one thing a visitor can tell apart.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/pages/{id}/actions/set_page_status
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_page_status

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Page · id of page
statusstring (enum)yesState · Allowed values: draft, published, archived

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
page_not_foundThe page must exist in this workspace.
no_changeIt must not already be in that state.
homepage_must_stay_publishedThe home page cannot be taken out of publication.

Events it publishes: site.page_status_changed

What it writes: page (update) — Changes the page state

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/set_page_status \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"status":"draft"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.page_status_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.setPageStatus("PAGE_ID", {
  status: "draft"
})
era pages set_page_status PAGE_ID \
  --status "draft"
tools/call
{
  "name": "era__site__set_page_status",
  "arguments": {
    "id": "PAGE_ID",
    "status": "draft"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 page_not_found, no_change, homepage_must_stay_published · 429 slow down

site.set_policy

Save a legal page — Privacy, terms or cookies — the three the storefront has routes for.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/policies/actions/set_policy
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_policy

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
policyTypestring (enum)yesWhich · Allowed values: privacy_policy, terms_of_service, cookie_policy
titlestringyesTitle · max 255
contentstringyesText · max 200000
isActivebooleanShown on the site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.

Events it publishes: site.policy_changed

What it writes: policy (upsert) — Writes the legal page

curl -X POST https://app.getera.io/api/v1/policies/actions/set_policy \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","policyType":"privacy_policy","title":"Title","content":"Text"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.policy_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.policies.setPolicy({
  siteId: "SITE_ID",
  policyType: "privacy_policy",
  title: "Title",
  content: "Text"
})
era policies set_policy \
  --site-id "SITE_ID" \
  --policy-type "privacy_policy" \
  --title "Title" \
  --content "Text"
tools/call
{
  "name": "era__site__set_policy",
  "arguments": {
    "siteId": "SITE_ID",
    "policyType": "privacy_policy",
    "title": "Title",
    "content": "Text"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found · 429 slow down

site.set_primary_domain

Make an address primary — The one a link to this site uses.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/domains/{id}/actions/set_primary_domain
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_primary_domain

Input

FieldTypeRequiredNotes
domainIduuidyesFilled from the path {id} · Address · id of domain

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
domain_not_foundThe address must be registered here.
not_verifiedOnly a verified address can be primary.
already_primaryIt must not already be the primary one.

Events it publishes: site.primary_domain_changed

What it writes: domain (update) — Moves the primary flag to it

curl -X POST https://app.getera.io/api/v1/domains/DOMAIN_ID/actions/set_primary_domain \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.primary_domain_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.domains.setPrimaryDomain("DOMAIN_ID")
era domains set_primary_domain DOMAIN_ID
tools/call
{
  "name": "era__site__set_primary_domain",
  "arguments": {
    "domainId": "DOMAIN_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 domain_not_found, not_verified, already_primary · 429 slow down

site.set_redirect

Add a redirect — Keeps an old address working after a page moves.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/page_redirects/actions/set_redirect
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_redirect

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
fromPathstringyesOld address · max 500
toPathstringyesGoes to · max 500
statusCodenumberKind · 301 for a permanent move, 302 while you are still deciding. · min 301 · max 308
notestringWhy · max 300

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_pathBoth sides must be paths on this site.
redirect_loopAn address may not redirect to itself.

Events it publishes: site.redirect_changed

What it writes: page_redirect (upsert) — Writes the redirect

curl -X POST https://app.getera.io/api/v1/page_redirects/actions/set_redirect \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","fromPath":"Old address","toPath":"Goes to"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.redirect_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pageRedirects.setRedirect({
  siteId: "SITE_ID",
  fromPath: "Old address",
  toPath: "Goes to"
})
era page_redirects set_redirect \
  --site-id "SITE_ID" \
  --from-path "Old address" \
  --to-path "Goes to"
tools/call
{
  "name": "era__site__set_redirect",
  "arguments": {
    "siteId": "SITE_ID",
    "fromPath": "Old address",
    "toPath": "Goes to"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_path, redirect_loop · 429 slow down

site.set_site_info

Save the company details — The name, logos and description every page of the site carries.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/site_infos/actions/set_site_info
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.set_site_info

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
companyNamestringyesCompany name · max 255
companyDescriptionstringAbout · max 8000
sloganstringSlogan · max 255
foundedYearnumberFounded · min 1800
footerTextstringFooter text · max 2000
footerCopyrightstringCopyright line · max 255
websiteUrlstringWebsite · max 500
metaDescriptionstringSearch description · max 2000
metaKeywordsstringKeywords · max 500
logoLightIduuidLogo (light) · id of file
logoDarkIduuidLogo (dark) · id of file
faviconIduuidFavicon · id of file

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
invalid_founded_yearA founding year is between 1800 and this year.
file_not_foundEvery image must be a file in this workspace.

Events it publishes: site.info_changed

What it writes: site_info (upsert) — Writes the one row this site has

curl -X POST https://app.getera.io/api/v1/site_infos/actions/set_site_info \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID","companyName":"Company name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.info_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteInfos.setSiteInfo({
  siteId: "SITE_ID",
  companyName: "Company name"
})
era site_infos set_site_info \
  --site-id "SITE_ID" \
  --company-name "Company name"
tools/call
{
  "name": "era__site__set_site_info",
  "arguments": {
    "siteId": "SITE_ID",
    "companyName": "Company name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, invalid_founded_year, file_not_found · 429 slow down

site.set_site_widgets

Set site widgets — WhatsApp, consent, language, dark mode, announcement and back-to-top switches.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/set_site_widgets
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_site_widgets

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
widgetsobject / arrayyesWidgets

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
archivedAn archived theme cannot be edited.
invalid_widgetsOnly known widgets with enabled switches are accepted.

Events it publishes: site.theme_changed

What it writes: theme (update) — Writes config.widgets

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/set_site_widgets \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"widgets":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.setSiteWidgets("THEME_ID", {
  widgets: {}
})
era themes set_site_widgets THEME_ID --data '{"widgets":{}}'
tools/call
{
  "name": "era__site__set_site_widgets",
  "arguments": {
    "themeId": "THEME_ID",
    "widgets": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, archived, invalid_widgets · 429 slow down

site.set_theme_seo

Set the search details — Site-wide search defaults, and the per-page overrides.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/set_theme_seo
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_theme_seo

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
seoobject / arraySite defaults
pageIduuidPage · id of page
pageSeoobject / arrayPage overrides

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
page_not_foundPer-page settings need a page on this site.
invalid_seoThe SEO sections are objects.
meta_title_too_longA meta title is at most 255 characters.

Events it publishes: site.theme_changed

What it writes: theme (update) — Writes config.seo and config.pageSeo

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/set_theme_seo \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"seo":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.setThemeSeo("THEME_ID", {
  seo: {}
})
era themes set_theme_seo THEME_ID --data '{"seo":{}}'
tools/call
{
  "name": "era__site__set_theme_seo",
  "arguments": {
    "themeId": "THEME_ID",
    "seo": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, page_not_found, invalid_seo, meta_title_too_long · 429 slow down

site.set_theme_tokens

Restyle a theme — Colours, type and spacing — the whole palette at once.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/themes/{id}/actions/set_theme_tokens
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.set_theme_tokens

Input

FieldTypeRequiredNotes
themeIduuidyesFilled from the path {id} · Theme · id of theme
colorsobject / arrayPalette
typographyobject / arrayTypography
layoutobject / arrayLayout
darkModeobject / arrayDark mode
motionobject / arrayMotion

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
theme_is_publishedA live theme cannot be edited in place.
missing_colour_tokenA palette carries all seventeen tokens.
unknown_fontA font must be one the runtime loads.
invalid_tokensSizes, scales and enums must be in range.

Events it publishes: site.theme_changed

What it writes: theme (update) — Writes the token sections of config

curl -X POST https://app.getera.io/api/v1/themes/THEME_ID/actions/set_theme_tokens \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"colors":{}}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.theme_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themes.setThemeTokens("THEME_ID", {
  colors: {}
})
era themes set_theme_tokens THEME_ID --data '{"colors":{}}'
tools/call
{
  "name": "era__site__set_theme_tokens",
  "arguments": {
    "themeId": "THEME_ID",
    "colors": {}
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, theme_is_published, missing_colour_token, unknown_font, invalid_tokens · 429 slow down

Show my website — The workspace’s sites with their live addresses and draft/published status — links that open on the phone (custom domain, the platform address, or the LAN address on a dev machine). Changes nothing.

Needs site:read · reads only

POSThttps://app.getera.io/api/v1/actions/site.share_links
API key or OAuth token · site:read
FieldTypeRequiredNotes
siteIduuidSite · Empty = every site. · id of site

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundA named site must exist in this workspace.

Events it publishes: site.links_shared

curl -X POST https://app.getera.io/api/v1/actions/site.share_links \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.links_shared"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("site.share_links", {
  siteId: "SITE_ID"
})
era site share_links \
  --site-id "SITE_ID"
tools/call
{
  "name": "era__site__share_links",
  "arguments": {
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found · 429 slow down

site.snapshot_theme

Save a site version — Keeps a copy of the theme as it is now, so it can be restored later.

Needs site:manage · removes or cannot be undone

POSThttps://app.getera.io/api/v1/theme_snapshots/actions/snapshot_theme
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.snapshot_theme

Input

FieldTypeRequiredNotes
themeIduuidyesTheme · id of theme
kindstring (enum)Kind · Allowed values: auto, manual, before
labelstringName · max 120

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
theme_not_foundThe theme must exist in this workspace.
label_requiredA named version needs a name.

What it writes: theme_snapshot (insert) — Stores a copy of the theme configuration; theme_snapshot (delete) — Drops the oldest automatic copies beyond the kept number

curl -X POST https://app.getera.io/api/v1/theme_snapshots/actions/snapshot_theme \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"themeId":"THEME_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": []
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.themeSnapshots.snapshotTheme({
  themeId: "THEME_ID"
})
era theme_snapshots snapshot_theme \
  --theme-id "THEME_ID"
tools/call
{
  "name": "era__site__snapshot_theme",
  "arguments": {
    "themeId": "THEME_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 theme_not_found, label_required · 429 slow down

site.start_deploy

Deploy a site — Starts a new version of the site on the edge (<slug>.getera.site). The upload itself happens around this action.

Needs site:manage · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/site_deploys/{id}/actions/start_deploy
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.start_deploy

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
kindstring (enum)Kind · Allowed values: static, theme
providerstring (enum)Where · Allowed values: cloudflare, local
sourcestring (enum)From · Allowed values: api, cli, desktop, chat, screen, rollback
fileCountnumberFiles · min 0
totalBytesnumberSize in bytes · min 0
hasWorkerbooleanHas a worker entry
manifestobject / arrayFiles and hashes
notestringNote · max 300
rollbackOfuuidFilled from the path {id} · Restore version · id of site_deploy

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
bundle_too_largeThe bundle must fit the edge limits (20,000 files, 500 MB).
deploy_in_progressOnly one deploy of a site runs at a time.
version_not_foundA version to restore must have gone live before.

Events it publishes: site.deploy_started

What it writes: site_deploy (insert) — A new version, uploading; site_deploy (update) — A stale upload is marked failed

curl -X POST https://app.getera.io/api/v1/site_deploys/SITE_DEPLOY_ID/actions/start_deploy \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"siteId":"SITE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.deploy_started"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.siteDeploys.startDeploy("SITE_DEPLOY_ID", {
  siteId: "SITE_ID"
})
era site_deploys start_deploy SITE_DEPLOY_ID \
  --site-id "SITE_ID"
tools/call
{
  "name": "era__site__start_deploy",
  "arguments": {
    "rollbackOf": "SITE_DEPLOY_ID",
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, bundle_too_large, deploy_in_progress, version_not_found · 429 slow down

site.submit_contact

Send a message — The contact form on the public site.

Needs site:write · reaches outside (sends, publishes or charges) · Also callable from a published site (visitor)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/contact_submissions/actions/submit_contact
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.submit_contact

Input

FieldTypeRequiredNotes
siteIduuidyesSite · id of site
fullnamestringyesName · max 255
emailemailyesEmail · max 255
subjectstringyesSubject · max 500
companystringCompany · max 255
phonephonePhone · max 50
countryCodenumberCountry code · min 1 · max 999
projectDetailsstringMessage · max 20000
userAgentstringBrowser · max 500
websitestringWebsite · Leave this empty. · max 255

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist.
honeypotThe decoy field must be empty.
missing_detailsA name and a subject are required.
rate_limitedOne address may only write so often.

Events it publishes: site.contact_submitted

What it writes: contact_submission (insert) — Records the message

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, honeypot, missing_details, rate_limited · 429 slow down

site.tag_post

Tag a post — Adds a tag and keeps its usage count honest.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/blog_tags/{id}/actions/tag_post
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.tag_post

Input

FieldTypeRequiredNotes
postIduuidyesPost · id of blog_post
tagIduuidyesFilled from the path {id} · Tag · id of blog_tag

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
post_not_foundThe post must exist in this workspace.
tag_not_foundThe tag must be on the same site.
already_taggedThe post must not already carry it.

Events it publishes: site.post_tagged

What it writes: blog_post_tag (insert) — Links the tag to the post; blog_tag (update) — Increments the usage count

curl -X POST https://app.getera.io/api/v1/blog_tags/BLOG_TAG_ID/actions/tag_post \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"postId":"BLOG_POST_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.post_tagged"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogTags.tagPost("BLOG_TAG_ID", {
  postId: "BLOG_POST_ID"
})
era blog_tags tag_post BLOG_TAG_ID \
  --post-id "BLOG_POST_ID"
tools/call
{
  "name": "era__site__tag_post",
  "arguments": {
    "tagId": "BLOG_TAG_ID",
    "postId": "BLOG_POST_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 post_not_found, tag_not_found, already_tagged · 429 slow down

site.unpublish

Take a site down — Visitors get “not published” rather than a 404.

Needs site:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/sites/{id}/actions/unpublish
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.unpublish

Input

FieldTypeRequiredNotes
siteIduuidyesFilled from the path {id} · Site · id of site

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
site_not_foundThe site must exist in this workspace.
no_changeIt must currently be live.

Events it publishes: site.unpublished

What it writes: site (update) — Marks it unpublished

curl -X POST https://app.getera.io/api/v1/sites/SITE_ID/actions/unpublish \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.unpublished"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.sites.unpublish("SITE_ID")
era sites unpublish SITE_ID
tools/call
{
  "name": "era__site__unpublish",
  "arguments": {
    "siteId": "SITE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 site_not_found, no_change · 429 slow down

site.unpublish_post

Take a post down — Back to a draft, or into the archive. The publication date is kept.

Needs site:write · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/blog_posts/{id}/actions/unpublish_post
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.unpublish_post

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Post · id of blog_post
statusstring (enum)State · Allowed values: draft, archived

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
post_not_foundThe post must exist in this workspace.
no_changeIt must not already be in that state.

Events it publishes: site.post_unpublished

What it writes: blog_post (update) — Changes the post state

curl -X POST https://app.getera.io/api/v1/blog_posts/BLOG_POST_ID/actions/unpublish_post \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"status":"draft"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.post_unpublished"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogPosts.unpublishPost("BLOG_POST_ID", {
  status: "draft"
})
era blog_posts unpublish_post BLOG_POST_ID \
  --status "draft"
tools/call
{
  "name": "era__site__unpublish_post",
  "arguments": {
    "id": "BLOG_POST_ID",
    "status": "draft"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 post_not_found, no_change · 429 slow down

site.untag_post

Remove a tag — Takes the tag off and decrements its usage count.

Needs site:write · removes or cannot be undone

POSThttps://app.getera.io/api/v1/blog_tags/{id}/actions/untag_post
API key or OAuth token · site:write
  • Canonical address: POST /api/v1/actions/site.untag_post

Input

FieldTypeRequiredNotes
postIduuidyesPost · id of blog_post
tagIduuidyesFilled from the path {id} · Tag · id of blog_tag

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
post_not_foundThe post must exist in this workspace.
not_taggedThe post must actually carry the tag.

Events it publishes: site.post_tagged

What it writes: blog_post_tag (delete) — Unlinks the tag; blog_tag (update) — Decrements the usage count

curl -X POST https://app.getera.io/api/v1/blog_tags/BLOG_TAG_ID/actions/untag_post \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"postId":"BLOG_POST_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.post_tagged"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blogTags.untagPost("BLOG_TAG_ID", {
  postId: "BLOG_POST_ID"
})
era blog_tags untag_post BLOG_TAG_ID \
  --post-id "BLOG_POST_ID"
tools/call
{
  "name": "era__site__untag_post",
  "arguments": {
    "tagId": "BLOG_TAG_ID",
    "postId": "BLOG_POST_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 post_not_found, not_tagged · 429 slow down

site.update_page

Edit a page — Title, address and search-engine details. Renaming a live page leaves a redirect.

Needs site:write · changes data

POSThttps://app.getera.io/api/v1/pages/{id}/actions/update_page
API key or OAuth token · site:write
  • REST alias: PATCH /api/v1/pages/{id}
  • Canonical address: POST /api/v1/actions/site.update_page

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Page · id of page
titlestringTitle · max 255
slugstringAddress · max 255
metaTitlestringSearch title · max 255
metaDescriptionstringSearch description · max 2000
metaKeywordsstringKeywords · max 500
sortOrdernumberPosition · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
page_not_foundThe page must exist in this workspace.
slug_takenThe new address must be free on this site.

Events it publishes: site.page_updated

What it writes: page (update) — Writes the page; page_redirect (upsert) — Keeps the old address working when a live page is renamed

curl -X POST https://app.getera.io/api/v1/pages/PAGE_ID/actions/update_page \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"title":"Title"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.page_updated"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.pages.updatePage("PAGE_ID", {
  title: "Title"
})
era pages update_page PAGE_ID \
  --title "Title"
tools/call
{
  "name": "era__site__update_page",
  "arguments": {
    "id": "PAGE_ID",
    "title": "Title"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 page_not_found, slug_taken · 429 slow down

site.verify_domain

Verify an address — Compares the TXT records the caller resolved against the token we issued.

Needs site:manage · changes data

POSThttps://app.getera.io/api/v1/domains/{id}/actions/verify_domain
API key or OAuth token · site:manage
  • Canonical address: POST /api/v1/actions/site.verify_domain

Input

FieldTypeRequiredNotes
domainIduuidyesFilled from the path {id} · Address · id of domain
observedTxtstringyesObserved TXT records · Comma-separated. The route resolves DNS; the action decides. · max 8000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
domain_not_foundThe address must be registered here.
already_verifiedIt must not already be verified.
no_tokenIt must have a verification record to look for.
token_not_foundThe record must be visible in DNS.

Events it publishes: site.domain_verified

What it writes: domain (update) — Marks it verified and clears the token

curl -X POST https://app.getera.io/api/v1/domains/DOMAIN_ID/actions/verify_domain \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"observedTxt":"Observed TXT records"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "site.domain_verified"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.domains.verifyDomain("DOMAIN_ID", {
  observedTxt: "Observed TXT records"
})
era domains verify_domain DOMAIN_ID \
  --observed-txt "Observed TXT records"
tools/call
{
  "name": "era__site__verify_domain",
  "arguments": {
    "domainId": "DOMAIN_ID",
    "observedTxt": "Observed TXT records"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold site:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 domain_not_found, already_verified, no_token, token_not_found · 429 slow down

Data models

BlogAuthor

Table blog_author · Resource blog_authors

  • GET /api/v1/blog_authors
  • GET /api/v1/blog_authors/{id}
  • PATCH /api/v1/blog_authors/{id} → site.save_blog_author
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
user_idstring (uuid) · uuidapp_user
display_namestring · text
biostring · textyes
is_publicboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, user_id

BlogCategory

Table blog_category · Resource blog_categories

  • GET /api/v1/blog_categories
  • GET /api/v1/blog_categories/{id}
  • PATCH /api/v1/blog_categories/{id} → site.save_blog_category
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
namestring · text
slugstring · text
descriptionstring · textyes
colorstring · textyes
iconstring · textyes
sort_orderinteger · int4
is_activeboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, slug

Checks:

  • CHECK (((color IS NULL) OR (color ~ '^#[0-9a-fA-F]{6}$'::text)))

BlogPostTag

Table blog_post_tag · Resource blog_post_tags

  • GET /api/v1/blog_post_tags
  • GET /api/v1/blog_post_tags/{id}
ColumnTypeNullableAllowed valuesReferences
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
post_idstring (uuid) · uuidblog_post
tag_idstring (uuid) · uuidblog_tag

Primary key: post_id, tag_id

BlogPost

Table blog_post · Resource blog_posts

  • GET /api/v1/blog_posts
  • GET /api/v1/blog_posts/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
titlestring · text
slugstring · text
excerptstring · textyes
contentobject · jsonb
statusstring · textdraft, published, archived
published_atstring (date-time) · timestamptzyes
author_idstring (uuid) · uuidyesblog_author
category_idstring (uuid) · uuidyesblog_category
meta_titlestring · textyes
meta_descriptionstring · textyes
meta_keywordsstring · textyes
view_countinteger · int4
is_featuredboolean · bool
featured_imagestring (uuid) · uuidyesfile
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
scheduled_atstring (date-time) · timestamptzyes

Primary key: id

Unique: site_id, slug

Checks:

  • CHECK ((jsonb_typeof(content) = 'object'::text))
  • CHECK ((view_count >= 0))

BlogTag

Table blog_tag · Resource blog_tags

  • GET /api/v1/blog_tags
  • GET /api/v1/blog_tags/{id}
  • PATCH /api/v1/blog_tags/{id} → site.save_blog_tag
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
namestring · text
slugstring · text
usage_countinteger · int4
is_publicboolean · bool
created_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, slug

Checks:

  • CHECK ((usage_count >= 0))

ClientReference

Table client_reference · Resource client_references

  • GET /api/v1/client_references
  • GET /api/v1/client_references/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
client_namestring · text
client_sectorstring · textyes
client_logostring (uuid) · uuidyesfile
project_titlestring · textyes
project_descriptionstring · textyes
project_imagesstring[] · uuid[]
project_urlstring · textyes
testimonialstring · textyes
testimonial_authorstring · textyes
testimonial_positionstring · textyes
is_featuredboolean · bool
is_publicboolean · bool
sort_orderinteger · int4
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

ContactInfo

Table contact_info · Resource contact_infos

  • GET /api/v1/contact_infos
  • GET /api/v1/contact_infos/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
emailstring · textyes
phonestring · textyes
addressstring · textyes
instagram_urlstring · textyes
tiktok_urlstring · textyes
linkedin_urlstring · textyes
facebook_urlstring · textyes
youtube_urlstring · textyes
twitter_urlstring · textyes
is_publicboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id

ContactSubmission

Table contact_submission · Resource contact_submissions

  • GET /api/v1/contact_submissions
  • GET /api/v1/contact_submissions/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
fullnamestring · text
companystring · textyes
emailstring · text
phonestring · textyes
country_codeinteger · int2yes
subjectstring · text
project_detailsstring · textyes
user_agentstring · textyes
statusstring · textnew, read, replied, archived, spam
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Domain

Table domain · Resource domains

  • GET /api/v1/domains
  • GET /api/v1/domains/{id}
  • POST /api/v1/domains → site.add_domain
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
hoststring · text
kindstring · text
is_primaryboolean · bool
statusstring · text
ssl_statusstring · text
created_atstring (date-time) · timestamptz
verified_atstring (date-time) · timestamptzyes

Primary key: id

Unique: host

FaqCategory

Table faq_category · Resource faq_categories

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
namestring · text
slugstring · text
descriptionstring · textyes
iconstring · textyes
sort_orderinteger · int4
is_activeboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, slug

FaqItem

Table faq_item · Resource faq_items

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
category_idstring (uuid) · uuidyesfaq_category
questionstring · text
answerstring · text
sort_orderinteger · int4
is_activeboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

News

Table news · Resource newses

  • GET /api/v1/newses
  • GET /api/v1/newses/{id}
  • PATCH /api/v1/newses/{id} → site.save_news
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
titlestring · text
slugstring · text
descriptionstring · textyes
contentobject · jsonb
statusstring · textdraft, published, archived
published_atstring (date-time) · timestamptzyes
created_bystring (uuid) · uuidyesapp_user
updated_bystring (uuid) · uuidyesapp_user
meta_titlestring · textyes
meta_descriptionstring · textyes
view_countinteger · int4
is_featuredboolean · bool
featured_imagestring (uuid) · uuidyesfile
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
scheduled_atstring (date-time) · timestamptzyes

Primary key: id

Unique: site_id, slug

Checks:

  • CHECK ((jsonb_typeof(content) = 'object'::text))
  • CHECK ((view_count >= 0))

PageRedirect

Table page_redirect · Resource page_redirects

  • GET /api/v1/page_redirects
  • GET /api/v1/page_redirects/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
from_pathstring · text
to_pathstring · text
status_codeinteger · int4
notestring · textyes
is_publicboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, from_path

Checks:

  • CHECK ((from_path ~~ '/%'::text))
  • CHECK ((from_path <> to_path))
  • CHECK ((status_code = ANY (ARRAY[301, 302, 307, 308])))
  • CHECK ((to_path ~~ '/%'::text))

Page

Table page · Resource pages

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
titlestring · text
slugstring · text
meta_titlestring · textyes
meta_descriptionstring · textyes
meta_keywordsstring · textyes
statusstring · textdraft, published, archived
parent_idstring (uuid) · uuidyespage
sort_orderinteger · int4
is_homepageboolean · bool
show_in_headerboolean · bool
show_in_footerboolean · bool
is_defaultboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, slug

Policy

Table policy · Resource policies

  • GET /api/v1/policies
  • GET /api/v1/policies/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
policy_typestring · textcookie_policy, privacy_policy, terms_of_service
titlestring · text
contentstring · text
is_activeboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, policy_type

SiteCodeTheme

Table site_code_theme · Resource site_code_themes

  • GET /api/v1/site_code_themes
  • GET /api/v1/site_code_themes/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
namestring · text
manifestobject · jsonb
versioninteger · int4
imported_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id

Checks:

  • CHECK ((jsonb_typeof(manifest) = 'object'::text))
  • CHECK (((length(name) >= 1) AND (length(name) <= 200)))

SiteContent

Table site_content · Resource site_contents

  • GET /api/v1/site_contents
  • GET /api/v1/site_contents/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
keystring · text
kindstring · texttext, rich_text, image, link, hours, list, number, boolean, json
sectionstring · text
labelstring · text
helpstring · textyes
valueobject · jsonb
sort_orderinteger · int4
is_publicboolean · bool
updated_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id, key

Checks:

  • CHECK (((help IS NULL) OR (length(help) <= 500)))
  • CHECK ((key ~ '^[a-z0-9][a-z0-9_.-]{0,99}$'::text))
  • CHECK (((length(label) >= 1) AND (length(label) <= 200)))
  • CHECK ((section ~ '^[a-z0-9][a-z0-9_-]{0,49}$'::text))
  • CHECK ((jsonb_typeof(value) = 'object'::text))

SiteDeploy

Table site_deploy · Resource site_deploys

  • GET /api/v1/site_deploys
  • GET /api/v1/site_deploys/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
versioninteger · int4
kindstring · textstatic, theme
providerstring · textcloudflare, local
statusstring · textuploading, live, failed, superseded, removed
urlstring · textyes
hoststring · textyes
script_namestring · textyes
provider_version_idstring · textyes
sourcestring · textapi, cli, desktop, chat, screen, rollback
rollback_ofstring (uuid) · uuidyessite_deploy
file_countinteger · int4
total_bytesstring (int64) · int8
has_workerboolean · bool
manifestobject · jsonb
logobject · jsonb
errorstring · textyes
notestring · textyes
created_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
finished_atstring (date-time) · timestamptzyes

Primary key: id

Unique: site_id, version

SiteInfo

Table site_info · Resource site_infos

  • GET /api/v1/site_infos
  • GET /api/v1/site_infos/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
company_namestring · text
company_descriptionstring · textyes
sloganstring · textyes
founded_yearinteger · int4yes
footer_textstring · textyes
footer_copyrightstring · textyes
website_urlstring · textyes
meta_descriptionstring · textyes
meta_keywordsstring[] · text[]
logo_lightstring (uuid) · uuidyesfile
logo_darkstring (uuid) · uuidyesfile
faviconstring (uuid) · uuidyesfile
is_publicboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: site_id

Checks:

  • CHECK (((founded_year IS NULL) OR ((founded_year >= 1800) AND ((founded_year)::numeric <= EXTRACT(year FROM CURRENT_DATE)))))

Site

Table site · Resource sites

  • GET /api/v1/sites
  • GET /api/v1/sites/{id}
  • POST /api/v1/sites → site.create_site
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
typestring · site_typebusiness, commerce, landing, portal
namestring · text
slugstring · text
localesstring[] · text[]
default_localestring · text
auth_modestring · text
publishedboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: workspace_id, slug

ThemeSnapshot

Table theme_snapshot · Resource theme_snapshots

  • GET /api/v1/theme_snapshots
  • GET /api/v1/theme_snapshots/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
theme_idstring (uuid) · uuidtheme
kindstring · textauto, manual, before
labelstring · textyes
configobject · jsonb
section_countinteger · int4
created_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz

Primary key: id

Theme

Table theme · Resource themes

  • GET /api/v1/themes
  • GET /api/v1/themes/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidsite
namestring · text
statusstring · text
versioninteger · int4
configobject · jsonb
schema_versionstring · text
parent_idstring (uuid) · uuidyestheme
custom_cssstring · textyes
created_atstring (date-time) · timestamptz
published_atstring (date-time) · timestamptzyes
updated_atstring (date-time) · timestamptz
scheduled_atstring (date-time) · timestamptzyes

Primary key: id

Events

Workflows and webhooks react to these events.

EventWhat happensPayload
site.auth_mode_changedA site changed its visitor sign-in mode.site_id, auth_mode
site.blocks_changedA page layout was saved into a draft theme.site_id, theme_id, page_id, blocks
site.blog_taxonomy_changedA blog category, tag or author changed.id, kind
site.contact_info_changedThe contact details changed.id, siteId
site.contact_submittedSomebody filled in the contact form. inbox opens a conversation on this.id, siteId, email
site.content_changedEditable content of a site changed (a rebuild/revalidate hook can listen to it).site_id, keys
site.createdA site was created.site_id, type, host
site.deploy_failedA deploy of a site failed.site_id, deploy_id, version, error
site.deploy_removedA deployed site was taken off the edge.site_id, deploy_id
site.deploy_startedA new version of a site started uploading to the edge.site_id, deploy_id, version
site.deployedA version of a site went live on the edge.site_id, deploy_id, version, url
site.domain_addedAn address was claimed for a site.domain_id, site_id, host
site.domain_removedAn address stopped pointing at a site.domain_id, host
site.domain_verifiedA custom domain passed verification.domain_id, host
site.faq_changedA question, group or ordering changed.id, kind
site.homepage_changedA different page became the home page.id, siteId
site.info_changedThe company details changed.id, siteId
site.links_sharedSomebody (or Era) asked where the sites can be seen.count
site.locale_changedA site changed its default or available languages.site_id, default_locale, locales
site.navigation_changedThe header or footer menu changed.id, siteId
site.news_publishedA news item changed state.id, status
site.news_savedA news item was written or edited.id, siteId, slug
site.page_createdA page was added.id, siteId, slug
site.page_deletedA page was deleted.id, siteId
site.page_movedA page moved in the page tree.id, parentId
site.page_status_changedA page was published, drafted or archived.id, status
site.page_updatedA page was edited.id, slug, redirectedFrom
site.policy_changedA legal page changed.id, siteId, policyType
site.post_publishedA blog post went live.id, siteId
site.post_savedA blog post was written or edited.id, siteId, slug
site.post_taggedA post gained or lost a tag.postId, tagId
site.post_unpublishedA blog post came down.id, status
site.primary_domain_changedA different address became the primary one.domain_id, host
site.publishedA site went live.site_id
site.redirect_changedA redirect was written or removed.id, siteId, fromPath
site.reference_deletedA reference was removed.id, siteId
site.reference_savedA reference was written or edited.id, siteId
site.submission_markedA message was marked read, replied, archived or spam.id, status
site.theme_changedA draft theme was restyled.site_id, theme_id
site.theme_draft_createdA draft theme was forked from the live one.site_id, theme_id, forked_from
site.theme_importedA code site’s theme manifest (era.theme.json) was imported.site_id, version, sections
site.theme_publishedA theme draft was published.site_id, theme_id
site.theme_scheduledA theme draft was scheduled to go live, or its schedule was cancelled.site_id, theme_id, publish_at
site.unpublishedA site was taken down.site_id
Was this page helpful?