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
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
blog_authors | blog_author | /api/v1/blog_authors | site.save_blog_author | ||
blog_categories | blog_category | /api/v1/blog_categories | site.save_blog_category | ||
blog_post_tags | blog_post_tag | /api/v1/blog_post_tags | |||
blog_posts | blog_post | /api/v1/blog_posts | |||
blog_tags | blog_tag | /api/v1/blog_tags | site.save_blog_tag | ||
client_references | client_reference | /api/v1/client_references | |||
contact_infos | contact_info | /api/v1/contact_infos | |||
contact_submissions | contact_submission | /api/v1/contact_submissions | |||
domains | domain | /api/v1/domains | site.add_domain | ||
faq_categories | faq_category | /api/v1/faq_categories | site.save_faq_category | site.delete_faq_category | |
faq_items | faq_item | /api/v1/faq_items | site.save_faq_item | site.delete_faq_item | |
newses | news | /api/v1/newses | site.save_news | ||
page_redirects | page_redirect | /api/v1/page_redirects | |||
pages | page | /api/v1/pages | site.update_page | site.delete_page | |
policies | policy | /api/v1/policies | |||
site_code_themes | site_code_theme | /api/v1/site_code_themes | |||
site_contents | site_content | /api/v1/site_contents | |||
site_deploys | site_deploy | /api/v1/site_deploys | |||
site_infos | site_info | /api/v1/site_infos | |||
sites | site | /api/v1/sites | site.create_site | ||
theme_snapshots | theme_snapshot | /api/v1/theme_snapshots | |||
themes | theme | /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
| Action | Needs | What it does |
|---|---|---|
site.add_domain | site:manage | Add an address |
site.add_page_block | site:write | Add a page section |
site.add_section | site:write | Add a section to a page |
site.assign_images | site:write | Assign image |
site.cancel_scheduled_content | site:write | Cancel scheduled publication |
site.cancel_theme_schedule | site:manage | Cancel a scheduled theme |
site.create_page | site:write | Add a page |
site.create_site | site:manage | Create a site |
site.create_theme_draft | site:manage | Start a draft |
site.define_content | site:manage | Declare the content a site uses |
site.delete_blog_taxonomy | site:manage | Delete blog category, tag or author |
site.delete_content | site:manage | Remove a content entry |
site.delete_faq_category | site:delete | Remove an FAQ group |
site.delete_faq_item | site:delete | Remove an FAQ question |
site.delete_page | site:delete | Delete a page |
site.delete_redirect | site:manage | Remove a redirect |
site.delete_reference | site:delete | Remove a reference |
site.edit_page_block | site:write | Edit a page section |
site.edit_section | site:write | Edit a section |
site.finish_deploy | site:manage | Record a deploy result |
site.import_theme | site:manage | Import a code site’s theme |
site.mark_submission | site:write | Mark a message |
site.move_page | site:write | Move a page |
site.move_page_block | site:write | Move a page section |
site.move_section | site:write | Move a section |
site.publish | site:manage | Take a site live |
site.publish_draft | site:manage | Publish the site’s changes |
site.publish_post | site:write | Publish a post |
site.publish_theme | site:manage | Publish a theme |
site.remove_deploy | site:manage | Take a deployed site down |
site.remove_domain | site:manage | Remove an address |
site.remove_page_block | site:write | Remove a page section |
site.remove_section | site:write | Remove a section |
site.reorder_blog_categories | site:manage | Reorder blog categories |
site.reorder_faq | site:write | Reorder the questions |
site.restore_theme_snapshot | site:write | Restore a site version |
site.restyle | site:write | Restyle the site |
site.rollback_theme | site:manage | Roll back to a theme |
site.save_blog_author | site:manage | Save an author |
site.save_blog_category | site:manage | Save a blog category |
site.save_blog_tag | site:manage | Save a blog tag |
site.save_faq_category | site:manage | Save an FAQ group |
site.save_faq_item | site:write | Save a question |
site.save_news | site:write | Write a news item |
site.save_post | site:write | Write a post |
site.save_reference | site:write | Save a reference |
site.save_theme_draft | site:write | Save a theme draft |
site.schedule_content | site:write | Schedule publication |
site.schedule_theme_publish | site:manage | Schedule a theme |
site.set_auth_mode | site:manage | Change visitor sign-in |
site.set_chrome | site:write | Set the header and footer |
site.set_contact_info | site:manage | Save the contact details |
site.set_content | site:manage | Change site content |
site.set_homepage | site:write | Make this the home page |
site.set_locale | site:manage | Set site languages |
site.set_news_status | site:write | Publish or archive news |
site.set_page_blocks | site:write | Save a page layout |
site.set_page_nav | site:write | Change the menu |
site.set_page_status | site:write | Publish or archive a page |
site.set_policy | site:manage | Save a legal page |
site.set_primary_domain | site:manage | Make an address primary |
site.set_redirect | site:manage | Add a redirect |
site.set_site_info | site:manage | Save the company details |
site.set_site_widgets | site:write | Set site widgets |
site.set_theme_seo | site:write | Set the search details |
site.set_theme_tokens | site:write | Restyle a theme |
site.share_links | site:read | Show my website |
site.snapshot_theme | site:manage | Save a site version |
site.start_deploy | site:manage | Deploy a site |
site.submit_contact | site:write | Send a message |
site.tag_post | site:write | Tag a post |
site.unpublish | site:manage | Take a site down |
site.unpublish_post | site:write | Take a post down |
site.untag_post | site:write | Remove a tag |
site.update_page | site:write | Edit a page |
site.verify_domain | site:manage | Verify 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
https://app.getera.io/api/v1/domains/actions/add_domain- REST alias:
POST /api/v1/domains - Canonical address:
POST /api/v1/actions/site.add_domain
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
host | string | yes | Hostname · max 253 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_host | It must be a hostname — no scheme, path or port. |
platform_domain | Platform addresses are issued with the site. |
host_taken | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/add_page_block- Canonical address:
POST /api/v1/actions/site.add_page_block
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
pageId | uuid | yes | Page · id of page |
blockId | string | yes | Section · max 120 |
type | string | yes | Block type · max 120 |
data | object / array | yes | Block data |
enabled | boolean | Visible | |
toIndex | number | Position · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
page_not_found | The page must be on the theme’s site. |
invalid_blocks | Every section needs a stable id, type and valid data. |
duplicate_block_id | Two 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/theme_snapshots/actions/add_section- Canonical address:
POST /api/v1/actions/site.add_section
Input
| Field | Type | Required | Notes |
|---|---|---|---|
site | string | Site · Id, address or name. Empty = the site edited last. · max 200 | |
page | string | Page · Id, address (/about), title or "home". Empty = home. · max 255 | |
type | string | yes | Kind of section · hero, features_grid, testimonials, faq, pricing, gallery, cta, contact_form, text, image, stats, team … · max 80 |
data | object / array | Its words and settings · { "title": "…", "subtitle": "…", "items": [ … ] } | |
position | number | Position from 1 · min 1 | |
after | string | After the section · max 200 | |
hidden | boolean | Add it hidden |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
page_not_found | The page must be on that site. |
unknown_block_type | The kind must be a section the site can draw. |
block_not_found | “After” must name a section on the page. |
invalid_blocks | Its 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"}'{
"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"{
"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
https://app.getera.io/api/v1/blog_posts/actions/assign_images- Canonical address:
POST /api/v1/actions/site.assign_images
Input
| Field | Type | Required | Notes |
|---|---|---|---|
ids | object / array | yes | Selected records |
fileId | uuid | yes | Image · id of file |
contentType | string (enum) | yes | Content · Allowed values: blog, news, reference |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
no_items | Choose one to 500 records. |
file_not_found | The file must be accessible in this workspace. |
image_required | The file must be an image. |
not_found | Every 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"}'{
"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"}'{
"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
https://app.getera.io/api/v1/blog_posts/actions/cancel_scheduled_content- Canonical address:
POST /api/v1/actions/site.cancel_scheduled_content
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Article |
kind | string (enum) | yes | Kind · Allowed values: post, news |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_scheduled | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/cancel_theme_schedule- Canonical address:
POST /api/v1/actions/site.cancel_theme_schedule
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_scheduled | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/pages/{id}/actions/create_page- Canonical address:
POST /api/v1/actions/site.create_page
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
title | string | yes | Title · max 255 |
slug | string | Address · Leave empty to take it from the title. · max 255 | |
parentId | uuid | Filled from the path {id} · Under · id of page | |
showInHeader | boolean | Show in the header menu | |
showInFooter | boolean | Show in the footer menu | |
sortOrder | number | Position · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
slug_taken | The address must be free on this site. |
parent_not_found | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/sites/actions/create_site- REST alias:
POST /api/v1/sites - Canonical address:
POST /api/v1/actions/site.create_site
Input
| Field | Type | Required | Notes |
|---|---|---|---|
type | string (enum) | Starting layout · Only the layout the site starts from. Any site can add shop pages later. · Allowed values: business, commerce, landing, portal | |
name | string | yes | Name · max 120 |
slug | string | Address · Leave empty to take it from the name. · max 63 | |
defaultLocale | string | Default language · max 12 | |
authMode | string (enum) | Visitor sign-in · Allowed values: guest, guest_with_login, login_required |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_slug | The name must produce a usable address. |
slug_taken | This workspace already has a site at that address. |
host_taken | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/actions/create_theme_draft- Canonical address:
POST /api/v1/actions/site.create_theme_draft
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
name | string | Name · max 120 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
draft_exists | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/site_contents/actions/define_content- Canonical address:
POST /api/v1/actions/site.define_content
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
entries | object / array | yes | Entries · [{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. |
overwrite | boolean | Replace values the owner changed |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_content | Every 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/blog_categories/actions/delete_blog_taxonomy- Canonical address:
POST /api/v1/actions/site.delete_blog_taxonomy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Entry |
kind | string (enum) | yes | Kind · Allowed values: category, tag, author |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
category_not_found | A category must exist. |
tag_not_found | A tag must exist. |
author_not_found | An author must exist. |
blog_category_in_use | A category assigned to posts stays. |
blog_tag_in_use | A tag assigned to posts stays. |
blog_author_in_use | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/site_contents/actions/delete_content- Canonical address:
POST /api/v1/actions/site.delete_content
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
key | string | yes | Entry · max 100 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
content_not_found | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/faq_categories/{id}/actions/delete_faq_category- REST alias:
DELETE /api/v1/faq_categories/{id} - Canonical address:
POST /api/v1/actions/site.delete_faq_category
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Group · id of faq_category |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
category_not_found | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/faq_items/{id}/actions/delete_faq_item- REST alias:
DELETE /api/v1/faq_items/{id} - Canonical address:
POST /api/v1/actions/site.delete_faq_item
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Question · id of faq_item |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
faq_item_not_found | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/pages/{id}/actions/delete_page- REST alias:
DELETE /api/v1/pages/{id} - Canonical address:
POST /api/v1/actions/site.delete_page
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Page · id of page |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
page_not_found | The page must exist in this workspace. |
page_is_default | A default page can only be archived. |
homepage_must_stay_published | The home page cannot be deleted. |
has_children | A 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/page_redirects/{id}/actions/delete_redirect- Canonical address:
POST /api/v1/actions/site.delete_redirect
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Redirect · id of page_redirect |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
redirect_not_found | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/client_references/{id}/actions/delete_reference- Canonical address:
POST /api/v1/actions/site.delete_reference
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Reference · id of client_reference |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
reference_not_found | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/themes/{id}/actions/edit_page_block- Canonical address:
POST /api/v1/actions/site.edit_page_block
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
pageId | uuid | yes | Page · id of page |
blockId | string | yes | Section · max 120 |
type | string | Block type · max 120 | |
data | object / array | Block data | |
enabled | boolean | Visible |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
page_not_found | The page must be on the theme’s site. |
block_not_found | The section must be on the page. |
invalid_blocks | Every section needs a stable id, type and valid data. |
duplicate_block_id | Two 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"}'{
"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"{
"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
https://app.getera.io/api/v1/theme_snapshots/actions/edit_section- Canonical address:
POST /api/v1/actions/site.edit_section
Input
| Field | Type | Required | Notes |
|---|---|---|---|
site | string | Site · Id, address or name. Empty = the site edited last. · max 200 | |
page | string | Page · Id, address (/about), title or "home". Empty = home. · max 255 | |
section | string | yes | Section · Its id, its position from 1, its kind (hero) or words from its title. · max 200 |
text | object / array | Words · { "title": "…", "subtitle": "…", "items.0.title": "…" } | |
image | string | Image link · An uploaded, generated or stock image: https://… or /… · max 2000 | |
imageField | string | Which image · Empty = the section’s main image; items.2.imageUrl for one card. · max 200 | |
data | object / array | Settings to merge | |
visible | boolean | Visible | |
hideOn | object / array | Hide on · ["phone"], ["tablet"], ["desktop"] | |
showOn | object / array | Show again on | |
locale | string | Language of the words · Empty = the site’s own language. · max 12 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
page_not_found | The page must be on that site. |
block_not_found | The section must be on the page. |
invalid_path | Words go into fields of the section, as text. |
invalid_image | An image is a plain https:// or / link. |
invalid_device | A device is phone, tablet or desktop. |
invalid_blocks | The section stays renderable. |
nothing_to_change | Something 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"}'{
"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"{
"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)
https://app.getera.io/api/v1/site_deploys/{id}/actions/finish_deploy- Canonical address:
POST /api/v1/actions/site.finish_deploy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Deploy · id of site_deploy |
ok | boolean | yes | Succeeded |
url | string | Address · max 500 | |
host | string | Host · max 253 | |
scriptName | string | Worker · max 63 | |
providerVersionId | string | Edge version · max 100 | |
log | object / array | Steps | |
error | string | Error · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
deploy_not_found | The deploy must exist in this workspace. |
not_uploading | Only 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}'{
"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{
"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
https://app.getera.io/api/v1/site_code_themes/actions/import_theme- Canonical address:
POST /api/v1/actions/site.import_theme
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
manifest | object / array | yes | era.theme.json |
dryRun | boolean | Only check it | |
prune | boolean | Remove settings the theme no longer declares |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_theme | The manifest needs a name and sections with ids, labels and settings of known types. |
invalid_content | Every 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/contact_submissions/{id}/actions/mark_submission- Canonical address:
POST /api/v1/actions/site.mark_submission
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Message · id of contact_submission |
status | string (enum) | yes | State · Allowed values: new, read, replied, archived, spam |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
submission_not_found | The message must exist in this workspace. |
no_change | It 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"}'{
"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"{
"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
https://app.getera.io/api/v1/pages/{id}/actions/move_page- Canonical address:
POST /api/v1/actions/site.move_page
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Page · id of page |
parentId | uuid | Under · id of page |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
page_not_found | The page must exist in this workspace. |
parent_not_found | The new parent must be on the same site. |
cannot_parent_to_self | A page cannot be its own parent. |
would_create_cycle | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/move_page_block- Canonical address:
POST /api/v1/actions/site.move_page_block
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
pageId | uuid | yes | Page · id of page |
blockId | string | yes | Section · max 120 |
toIndex | number | yes | Position · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
page_not_found | The page must be on the theme’s site. |
block_not_found | The section must be on the page. |
invalid_blocks | Every section needs a stable id, type and valid data. |
duplicate_block_id | Two 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}'{
"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{
"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
https://app.getera.io/api/v1/theme_snapshots/actions/move_section- Canonical address:
POST /api/v1/actions/site.move_section
Input
| Field | Type | Required | Notes |
|---|---|---|---|
site | string | Site · Id, address or name. Empty = the site edited last. · max 200 | |
page | string | Page · Id, address (/about), title or "home". Empty = home. · max 255 | |
section | string | yes | Section · Its id, its position from 1, its kind (hero) or words from its title. · max 200 |
position | number | New position from 1 · min 1 | |
direction | string (enum) | Or · Allowed values: up, down, top, bottom |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
page_not_found | The page must be on that site. |
block_not_found | The section must be on the page. |
no_position | A position or a direction is given. |
nothing_to_change | It 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"}'{
"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"{
"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)
https://app.getera.io/api/v1/sites/{id}/actions/publish- Canonical address:
POST /api/v1/actions/site.publish
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Filled from the path {id} · Site · id of site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
no_change | It must not already be live. |
no_verified_domain | At 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/themes/actions/publish_draft- Canonical address:
POST /api/v1/actions/site.publish_draft
Input
| Field | Type | Required | Notes |
|---|---|---|---|
site | string | Site · Id, address or name. Empty = the site edited last. · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
no_draft | There 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"}'{
"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"{
"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)
https://app.getera.io/api/v1/blog_posts/{id}/actions/publish_post- Canonical address:
POST /api/v1/actions/site.publish_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Post · id of blog_post |
scheduledOnly | boolean | Scheduled run |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
post_not_found | The post must exist in this workspace. |
no_change | It must not already be published. |
empty_content | An empty post cannot be published. |
not_scheduled | An 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}'{
"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{
"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)
https://app.getera.io/api/v1/themes/{id}/actions/publish_theme- Canonical address:
POST /api/v1/actions/site.publish_theme
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
already_published | It must not already be live. |
archived | An 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/site_deploys/actions/remove_deploy- Canonical address:
POST /api/v1/actions/site.remove_deploy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_deployed | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/domains/{id}/actions/remove_domain- Canonical address:
POST /api/v1/actions/site.remove_domain
Input
| Field | Type | Required | Notes |
|---|---|---|---|
domainId | uuid | yes | Filled from the path {id} · Address · id of domain |
force | boolean | Remove anyway |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
domain_not_found | The address must be registered here. |
is_primary | The primary address cannot be removed while others exist. |
last_domain | Removing 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}'{
"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{
"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
https://app.getera.io/api/v1/themes/{id}/actions/remove_page_block- Canonical address:
POST /api/v1/actions/site.remove_page_block
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
pageId | uuid | yes | Page · id of page |
blockId | string | yes | Section · max 120 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
page_not_found | The page must be on the theme’s site. |
block_not_found | The section must be on the page. |
invalid_blocks | Every section needs a stable id, type and valid data. |
duplicate_block_id | Two 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"}'{
"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"{
"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
https://app.getera.io/api/v1/theme_snapshots/actions/remove_section- Canonical address:
POST /api/v1/actions/site.remove_section
Input
| Field | Type | Required | Notes |
|---|---|---|---|
site | string | Site · Id, address or name. Empty = the site edited last. · max 200 | |
page | string | Page · Id, address (/about), title or "home". Empty = home. · max 255 | |
section | string | yes | Section · 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:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
page_not_found | The page must be on that site. |
block_not_found | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/blog_categories/actions/reorder_blog_categories- Canonical address:
POST /api/v1/actions/site.reorder_blog_categories
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
ids | object / array | yes | Categories in order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_blog_category_order | Every 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/faq_items/actions/reorder_faq- Canonical address:
POST /api/v1/actions/site.reorder_faq
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
ids | object / array | yes | New order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_order | The new order must be a list of question ids. |
unknown_faq_item | Every 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/themes/actions/restore_theme_snapshot- Canonical address:
POST /api/v1/actions/site.restore_theme_snapshot
Input
| Field | Type | Required | Notes |
|---|---|---|---|
snapshotId | uuid | yes | Version |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
snapshot_not_found | The saved version must exist in this workspace. |
no_draft | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/actions/restyle- Canonical address:
POST /api/v1/actions/site.restyle
Input
| Field | Type | Required | Notes |
|---|---|---|---|
site | string | Site · Id, address or name. Empty = the site edited last. · max 200 | |
brandColor | string | Brand colour · #1f6feb — buttons, links and accents follow it. · max 9 | |
colors | object / array | Colours · Any of background, foreground, primary, primaryForeground, secondary, secondaryForeground, accent, accentForeground, muted, mutedForeground, border, input, ring, card, cardForeground, destructive, destructiveForeground as #rrggbb. | |
darkColors | object / array | Dark-mode colours · The same tokens, for the dark scheme. | |
headingFont | string | Heading font · inter, geist, system, serif, mono, playfair, poppins, montserrat, dm-serif, roboto, fraunces, manrope · max 40 | |
bodyFont | string | Text font · max 40 | |
baseSize | number | Text size (px) | |
radius | number | Corner roundness (0–2) | |
spacing | string (enum) | Spacing · Allowed values: compact, default, relaxed | |
containerWidth | string (enum) | Content width · Allowed values: narrow, default, wide, full | |
buttonStyle | string (enum) | Buttons · Allowed values: default, pill, square | |
dark | boolean | Dark mode |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_color | Colours are known theme colours, as #rrggbb. |
unknown_font | A font must be one the site loads. |
invalid_tokens | Sizes and choices are in range. |
nothing_to_change | Something 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/rollback_theme- Canonical address:
POST /api/v1/actions/site.rollback_theme
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
already_published | It must not already be live. |
not_archived | Rolling 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/blog_authors/{id}/actions/save_blog_author- REST alias:
PATCH /api/v1/blog_authors/{id} - Canonical address:
POST /api/v1/actions/site.save_blog_author
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Author · id of blog_author | |
userId | uuid | yes | Person · id of app_user |
displayName | string | yes | Shown as · max 120 |
bio | string | Introduction · max 4000 | |
isPublic | boolean | Visible on the site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
user_not_found | The person must be in this organization. |
author_exists | One author entry per person per site. |
author_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/blog_categories/{id}/actions/save_blog_category- REST alias:
PATCH /api/v1/blog_categories/{id} - Canonical address:
POST /api/v1/actions/site.save_blog_category
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Category · id of blog_category | |
name | string | yes | Name · max 100 |
slug | string | Address · max 100 | |
description | string | Description · max 2000 | |
color | string | Colour · max 7 | |
icon | string | Icon · max 50 | |
sortOrder | number | Position · min 0 | |
isActive | boolean | In use |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_slug | The name must produce a usable address. |
slug_taken | The address must be free on this site. |
invalid_colour | A colour must be a six-digit hex value. |
category_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/blog_tags/{id}/actions/save_blog_tag- REST alias:
PATCH /api/v1/blog_tags/{id} - Canonical address:
POST /api/v1/actions/site.save_blog_tag
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Tag · id of blog_tag | |
name | string | yes | Name · max 50 |
slug | string | Address · max 50 | |
isPublic | boolean | Visible on the site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_slug | The name must produce a usable address. |
slug_taken | The address must be free on this site. |
tag_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/faq_categories/{id}/actions/save_faq_category- REST alias:
PATCH /api/v1/faq_categories/{id} - Canonical address:
POST /api/v1/actions/site.save_faq_category
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Group · id of faq_category | |
name | string | yes | Name · max 100 |
slug | string | Address · max 100 | |
description | string | Description · max 2000 | |
icon | string | Icon · max 50 | |
sortOrder | number | Position · min 0 | |
isActive | boolean | Shown on the site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_slug | The name must produce a usable address. |
slug_taken | The address must be free on this site. |
category_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/faq_items/{id}/actions/save_faq_item- REST alias:
PATCH /api/v1/faq_items/{id} - Canonical address:
POST /api/v1/actions/site.save_faq_item
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Question · id of faq_item | |
categoryId | uuid | Group · id of faq_category | |
question | string | yes | Question · max 1000 |
answer | string | yes | Answer · max 20000 |
sortOrder | number | Position · min 0 | |
isActive | boolean | Shown on the site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
category_not_found | A group must be on the same site. |
faq_item_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/newses/{id}/actions/save_news- REST alias:
PATCH /api/v1/newses/{id} - Canonical address:
POST /api/v1/actions/site.save_news
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · News item · id of news | |
title | string | yes | Title · max 255 |
slug | string | Address · max 255 | |
description | string | Summary · max 2000 | |
content | object / array | Body | |
metaTitle | string | Search title · max 255 | |
metaDescription | string | Search description · max 2000 | |
isFeatured | boolean | Featured | |
featuredImageId | uuid | Cover image · id of file | |
publishedAt | date | Publication date |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_slug | The title must produce a usable address. |
slug_taken | The address must be free on this site. |
news_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/blog_posts/{id}/actions/save_post- Canonical address:
POST /api/v1/actions/site.save_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Post · id of blog_post | |
title | string | yes | Title · max 255 |
slug | string | Address · max 255 | |
excerpt | string | Excerpt · max 2000 | |
content | object / array | Body | |
categoryId | uuid | Category · id of blog_category | |
authorId | uuid | Author · id of blog_author | |
metaTitle | string | Search title · max 255 | |
metaDescription | string | Search description · max 2000 | |
metaKeywords | string | Keywords · max 500 | |
isFeatured | boolean | Featured | |
featuredImageId | uuid | Cover image · id of file |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_slug | The title must produce a usable address. |
slug_taken | The address must be free on this site. |
category_not_found | A category must be on the same site. |
author_not_found | An author must be on the same site. |
post_not_found | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/client_references/{id}/actions/save_reference- Canonical address:
POST /api/v1/actions/site.save_reference
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
id | uuid | Filled from the path {id} · Reference · id of client_reference | |
clientName | string | yes | Client · max 255 |
clientSector | string | Sector · max 100 | |
clientLogoId | uuid | Logo · id of file | |
projectTitle | string | Project · max 255 | |
projectDescription | string | What you did · max 8000 | |
projectImages | object / array | Project images | |
projectUrl | string | Link · max 500 | |
testimonial | string | Quote · max 4000 | |
testimonialAuthor | string | Said by · max 255 | |
testimonialPosition | string | Their role · max 100 | |
isFeatured | boolean | Featured | |
isPublic | boolean | Shown on the site | |
sortOrder | number | Position · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
client_name_required | A reference needs a client name. |
reference_not_found | An edit must name a reference that exists. |
invalid_reference_images | Project images must be a list of file ids. |
reference_file_not_found | Every 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/save_theme_draft- Canonical address:
POST /api/v1/actions/site.save_theme_draft
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
config | object / array | yes | Theme configuration |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
invalid_theme | The editor sends one versioned theme configuration. |
missing_colour_token | A palette carries all seventeen tokens. |
unknown_font | A font must be one the runtime loads. |
invalid_tokens | Theme tokens and sizes match the runtime contract. |
invalid_chrome | Header and footer blocks and positions have valid shapes. |
unknown_variant | Legacy chrome uses a layout the renderer draws. |
page_not_found | Every page layout belongs to this site. |
invalid_blocks | Every page body is a list of renderable blocks. |
duplicate_block_id | Two blocks on a page may not share an id. |
invalid_seo | SEO sections are objects keyed by page. |
meta_title_too_long | A 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/blog_posts/actions/schedule_content- Canonical address:
POST /api/v1/actions/site.schedule_content
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Article |
kind | string (enum) | yes | Kind · Allowed values: post, news |
publishAt | date-time | yes | Publish at |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
post_not_found | A blog post must exist. |
news_not_found | A news item must exist. |
empty_content | An article needs content. |
publish_time_in_past | The 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"}'{
"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"{
"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)
https://app.getera.io/api/v1/themes/{id}/actions/schedule_theme_publish- Canonical address:
POST /api/v1/actions/site.schedule_theme_publish
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
publishAt | date-time | yes | Publish at |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
not_a_draft | Only a draft can be scheduled. |
publish_time_in_past | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/sites/{id}/actions/set_auth_mode- Canonical address:
POST /api/v1/actions/site.set_auth_mode
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Filled from the path {id} · Site · id of site |
authMode | string (enum) | yes | Visitor sign-in · Allowed values: guest, guest_with_login, login_required |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/set_chrome- Canonical address:
POST /api/v1/actions/site.set_chrome
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
header | object / array | Header | |
footer | object / array | Footer | |
headerPosition | number | Header sits at · min 0 | |
footerPosition | number | Footer sits at · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
invalid_chrome | Header and footer are objects; positions are indexes. |
unknown_variant | The 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/contact_infos/actions/set_contact_info- Canonical address:
POST /api/v1/actions/site.set_contact_info
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
email | Email · max 255 | ||
phone | phone | Phone · max 50 | |
address | string | Address · max 1000 | |
instagramUrl | string | Instagram · max 500 | |
tiktokUrl | string | TikTok · max 500 | |
linkedinUrl | string | LinkedIn · max 500 | |
facebookUrl | string | Facebook · max 500 | |
youtubeUrl | string | YouTube · max 500 | |
twitterUrl | string | X · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_url | Every 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"}'{
"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"{
"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
https://app.getera.io/api/v1/site_contents/actions/set_content- Canonical address:
POST /api/v1/actions/site.set_content
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
key | string | yes | Entry · max 100 |
value | object / array | yes | Value |
locale | string | Language · Only this language (tr, en …); leave empty to send every language. · max 5 | |
label | string | Name · max 200 | |
isPublic | boolean | Shown on the site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
content_not_found | The entry must exist on the site (declare it with site.define_content). |
invalid_content | The 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/pages/{id}/actions/set_homepage- Canonical address:
POST /api/v1/actions/site.set_homepage
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Page · id of page |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
page_not_found | The page must exist in this workspace. |
no_change | It must not already be the home page. |
page_not_published | Only 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/sites/{id}/actions/set_locale- Canonical address:
POST /api/v1/actions/site.set_locale
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Filled from the path {id} · Site · id of site |
defaultLocale | string | yes | Default language · max 16 |
locales | object / array | Available languages |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_locale | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/newses/{id}/actions/set_news_status- Canonical address:
POST /api/v1/actions/site.set_news_status
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · News item · id of news |
status | string (enum) | yes | State · Allowed values: draft, published, archived |
scheduledOnly | boolean | Scheduled run |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
news_not_found | The item must exist in this workspace. |
no_change | It must not already be in that state. |
empty_content | An empty item cannot be published. |
not_scheduled | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/set_page_blocks- Canonical address:
POST /api/v1/actions/site.set_page_blocks
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
pageId | uuid | yes | Page · id of page |
blocks | object / array | yes | Blocks |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
page_not_found | The page must be on the theme’s site. |
invalid_blocks | Every block needs an id, a type and a boolean enabled. |
duplicate_block_id | Two 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/pages/{id}/actions/set_page_nav- Canonical address:
POST /api/v1/actions/site.set_page_nav
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Page · id of page |
showInHeader | boolean | Header menu | |
showInFooter | boolean | Footer menu | |
sortOrder | number | Position · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
page_not_found | The 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}'{
"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{
"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
https://app.getera.io/api/v1/pages/{id}/actions/set_page_status- Canonical address:
POST /api/v1/actions/site.set_page_status
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Page · id of page |
status | string (enum) | yes | State · Allowed values: draft, published, archived |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
page_not_found | The page must exist in this workspace. |
no_change | It must not already be in that state. |
homepage_must_stay_published | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/policies/actions/set_policy- Canonical address:
POST /api/v1/actions/site.set_policy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
policyType | string (enum) | yes | Which · Allowed values: privacy_policy, terms_of_service, cookie_policy |
title | string | yes | Title · max 255 |
content | string | yes | Text · max 200000 |
isActive | boolean | Shown on the site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/domains/{id}/actions/set_primary_domain- Canonical address:
POST /api/v1/actions/site.set_primary_domain
Input
| Field | Type | Required | Notes |
|---|---|---|---|
domainId | uuid | yes | Filled from the path {id} · Address · id of domain |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
domain_not_found | The address must be registered here. |
not_verified | Only a verified address can be primary. |
already_primary | It 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/page_redirects/actions/set_redirect- Canonical address:
POST /api/v1/actions/site.set_redirect
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
fromPath | string | yes | Old address · max 500 |
toPath | string | yes | Goes to · max 500 |
statusCode | number | Kind · 301 for a permanent move, 302 while you are still deciding. · min 301 · max 308 | |
note | string | Why · max 300 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_path | Both sides must be paths on this site. |
redirect_loop | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/site_infos/actions/set_site_info- Canonical address:
POST /api/v1/actions/site.set_site_info
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
companyName | string | yes | Company name · max 255 |
companyDescription | string | About · max 8000 | |
slogan | string | Slogan · max 255 | |
foundedYear | number | Founded · min 1800 | |
footerText | string | Footer text · max 2000 | |
footerCopyright | string | Copyright line · max 255 | |
websiteUrl | string | Website · max 500 | |
metaDescription | string | Search description · max 2000 | |
metaKeywords | string | Keywords · max 500 | |
logoLightId | uuid | Logo (light) · id of file | |
logoDarkId | uuid | Logo (dark) · id of file | |
faviconId | uuid | Favicon · id of file |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
invalid_founded_year | A founding year is between 1800 and this year. |
file_not_found | Every 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"}'{
"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"{
"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
https://app.getera.io/api/v1/themes/{id}/actions/set_site_widgets- Canonical address:
POST /api/v1/actions/site.set_site_widgets
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
widgets | object / array | yes | Widgets |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
archived | An archived theme cannot be edited. |
invalid_widgets | Only 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/themes/{id}/actions/set_theme_seo- Canonical address:
POST /api/v1/actions/site.set_theme_seo
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
seo | object / array | Site defaults | |
pageId | uuid | Page · id of page | |
pageSeo | object / array | Page overrides |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
page_not_found | Per-page settings need a page on this site. |
invalid_seo | The SEO sections are objects. |
meta_title_too_long | A 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":{}}'{
"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":{}}'{
"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
https://app.getera.io/api/v1/themes/{id}/actions/set_theme_tokens- Canonical address:
POST /api/v1/actions/site.set_theme_tokens
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Filled from the path {id} · Theme · id of theme |
colors | object / array | Palette | |
typography | object / array | Typography | |
layout | object / array | Layout | |
darkMode | object / array | Dark mode | |
motion | object / array | Motion |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
theme_is_published | A live theme cannot be edited in place. |
missing_colour_token | A palette carries all seventeen tokens. |
unknown_font | A font must be one the runtime loads. |
invalid_tokens | Sizes, 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":{}}'{
"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":{}}'{
"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
site.share_links
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
https://app.getera.io/api/v1/actions/site.share_linksInput
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | Site · Empty = every site. · id of site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/theme_snapshots/actions/snapshot_theme- Canonical address:
POST /api/v1/actions/site.snapshot_theme
Input
| Field | Type | Required | Notes |
|---|---|---|---|
themeId | uuid | yes | Theme · id of theme |
kind | string (enum) | Kind · Allowed values: auto, manual, before | |
label | string | Name · max 120 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
theme_not_found | The theme must exist in this workspace. |
label_required | A 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"}'{
"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"{
"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)
https://app.getera.io/api/v1/site_deploys/{id}/actions/start_deploy- Canonical address:
POST /api/v1/actions/site.start_deploy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
kind | string (enum) | Kind · Allowed values: static, theme | |
provider | string (enum) | Where · Allowed values: cloudflare, local | |
source | string (enum) | From · Allowed values: api, cli, desktop, chat, screen, rollback | |
fileCount | number | Files · min 0 | |
totalBytes | number | Size in bytes · min 0 | |
hasWorker | boolean | Has a worker entry | |
manifest | object / array | Files and hashes | |
note | string | Note · max 300 | |
rollbackOf | uuid | Filled from the path {id} · Restore version · id of site_deploy |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
bundle_too_large | The bundle must fit the edge limits (20,000 files, 500 MB). |
deploy_in_progress | Only one deploy of a site runs at a time. |
version_not_found | A 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/contact_submissions/actions/submit_contact- Canonical address:
POST /api/v1/actions/site.submit_contact
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Site · id of site |
fullname | string | yes | Name · max 255 |
email | yes | Email · max 255 | |
subject | string | yes | Subject · max 500 |
company | string | Company · max 255 | |
phone | phone | Phone · max 50 | |
countryCode | number | Country code · min 1 · max 999 | |
projectDetails | string | Message · max 20000 | |
userAgent | string | Browser · max 500 | |
website | string | Website · Leave this empty. · max 255 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist. |
honeypot | The decoy field must be empty. |
missing_details | A name and a subject are required. |
rate_limited | One 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
https://app.getera.io/api/v1/blog_tags/{id}/actions/tag_post- Canonical address:
POST /api/v1/actions/site.tag_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
postId | uuid | yes | Post · id of blog_post |
tagId | uuid | yes | Filled from the path {id} · Tag · id of blog_tag |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
post_not_found | The post must exist in this workspace. |
tag_not_found | The tag must be on the same site. |
already_tagged | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/sites/{id}/actions/unpublish- Canonical address:
POST /api/v1/actions/site.unpublish
Input
| Field | Type | Required | Notes |
|---|---|---|---|
siteId | uuid | yes | Filled from the path {id} · Site · id of site |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
site_not_found | The site must exist in this workspace. |
no_change | It 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 '{}'{
"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{
"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)
https://app.getera.io/api/v1/blog_posts/{id}/actions/unpublish_post- Canonical address:
POST /api/v1/actions/site.unpublish_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Post · id of blog_post |
status | string (enum) | State · Allowed values: draft, archived |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
post_not_found | The post must exist in this workspace. |
no_change | It 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"}'{
"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"{
"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
https://app.getera.io/api/v1/blog_tags/{id}/actions/untag_post- Canonical address:
POST /api/v1/actions/site.untag_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
postId | uuid | yes | Post · id of blog_post |
tagId | uuid | yes | Filled from the path {id} · Tag · id of blog_tag |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
post_not_found | The post must exist in this workspace. |
not_tagged | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/pages/{id}/actions/update_page- REST alias:
PATCH /api/v1/pages/{id} - Canonical address:
POST /api/v1/actions/site.update_page
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Page · id of page |
title | string | Title · max 255 | |
slug | string | Address · max 255 | |
metaTitle | string | Search title · max 255 | |
metaDescription | string | Search description · max 2000 | |
metaKeywords | string | Keywords · max 500 | |
sortOrder | number | Position · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
page_not_found | The page must exist in this workspace. |
slug_taken | The 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"}'{
"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"{
"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
https://app.getera.io/api/v1/domains/{id}/actions/verify_domain- Canonical address:
POST /api/v1/actions/site.verify_domain
Input
| Field | Type | Required | Notes |
|---|---|---|---|
domainId | uuid | yes | Filled from the path {id} · Address · id of domain |
observedTxt | string | yes | Observed 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:
| Code | Refused when |
|---|---|
domain_not_found | The address must be registered here. |
already_verified | It must not already be verified. |
no_token | It must have a verification record to look for. |
token_not_found | The 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"}'{
"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"{
"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_authorsGET /api/v1/blog_authors/{id}PATCH /api/v1/blog_authors/{id}→site.save_blog_author
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
user_id | string (uuid) · uuid | app_user | ||
display_name | string · text | |||
bio | string · text | yes | ||
is_public | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: site_id, user_id
BlogCategory
Table blog_category · Resource blog_categories
GET /api/v1/blog_categoriesGET /api/v1/blog_categories/{id}PATCH /api/v1/blog_categories/{id}→site.save_blog_category
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
name | string · text | |||
slug | string · text | |||
description | string · text | yes | ||
color | string · text | yes | ||
icon | string · text | yes | ||
sort_order | integer · int4 | |||
is_active | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (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_tagsGET /api/v1/blog_post_tags/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
post_id | string (uuid) · uuid | blog_post | ||
tag_id | string (uuid) · uuid | blog_tag |
Primary key: post_id, tag_id
BlogPost
Table blog_post · Resource blog_posts
GET /api/v1/blog_postsGET /api/v1/blog_posts/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
title | string · text | |||
slug | string · text | |||
excerpt | string · text | yes | ||
content | object · jsonb | |||
status | string · text | draft, published, archived | ||
published_at | string (date-time) · timestamptz | yes | ||
author_id | string (uuid) · uuid | yes | blog_author | |
category_id | string (uuid) · uuid | yes | blog_category | |
meta_title | string · text | yes | ||
meta_description | string · text | yes | ||
meta_keywords | string · text | yes | ||
view_count | integer · int4 | |||
is_featured | boolean · bool | |||
featured_image | string (uuid) · uuid | yes | file | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
scheduled_at | string (date-time) · timestamptz | yes |
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_tagsGET /api/v1/blog_tags/{id}PATCH /api/v1/blog_tags/{id}→site.save_blog_tag
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
name | string · text | |||
slug | string · text | |||
usage_count | integer · int4 | |||
is_public | boolean · bool | |||
created_at | string (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_referencesGET /api/v1/client_references/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
client_name | string · text | |||
client_sector | string · text | yes | ||
client_logo | string (uuid) · uuid | yes | file | |
project_title | string · text | yes | ||
project_description | string · text | yes | ||
project_images | string[] · uuid[] | |||
project_url | string · text | yes | ||
testimonial | string · text | yes | ||
testimonial_author | string · text | yes | ||
testimonial_position | string · text | yes | ||
is_featured | boolean · bool | |||
is_public | boolean · bool | |||
sort_order | integer · int4 | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
ContactInfo
Table contact_info · Resource contact_infos
GET /api/v1/contact_infosGET /api/v1/contact_infos/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
email | string · text | yes | ||
phone | string · text | yes | ||
address | string · text | yes | ||
instagram_url | string · text | yes | ||
tiktok_url | string · text | yes | ||
linkedin_url | string · text | yes | ||
facebook_url | string · text | yes | ||
youtube_url | string · text | yes | ||
twitter_url | string · text | yes | ||
is_public | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: site_id
ContactSubmission
Table contact_submission · Resource contact_submissions
GET /api/v1/contact_submissionsGET /api/v1/contact_submissions/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
fullname | string · text | |||
company | string · text | yes | ||
email | string · text | |||
phone | string · text | yes | ||
country_code | integer · int2 | yes | ||
subject | string · text | |||
project_details | string · text | yes | ||
user_agent | string · text | yes | ||
status | string · text | new, read, replied, archived, spam | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Domain
Table domain · Resource domains
GET /api/v1/domainsGET /api/v1/domains/{id}POST /api/v1/domains→site.add_domain
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
host | string · text | |||
kind | string · text | |||
is_primary | boolean · bool | |||
status | string · text | |||
ssl_status | string · text | |||
created_at | string (date-time) · timestamptz | |||
verified_at | string (date-time) · timestamptz | yes |
Primary key: id
Unique: host
FaqCategory
Table faq_category · Resource faq_categories
GET /api/v1/faq_categoriesGET /api/v1/faq_categories/{id}PATCH /api/v1/faq_categories/{id}→site.save_faq_categoryDELETE /api/v1/faq_categories/{id}→site.delete_faq_category
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
name | string · text | |||
slug | string · text | |||
description | string · text | yes | ||
icon | string · text | yes | ||
sort_order | integer · int4 | |||
is_active | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: site_id, slug
FaqItem
Table faq_item · Resource faq_items
GET /api/v1/faq_itemsGET /api/v1/faq_items/{id}PATCH /api/v1/faq_items/{id}→site.save_faq_itemDELETE /api/v1/faq_items/{id}→site.delete_faq_item
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
category_id | string (uuid) · uuid | yes | faq_category | |
question | string · text | |||
answer | string · text | |||
sort_order | integer · int4 | |||
is_active | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
News
Table news · Resource newses
GET /api/v1/newsesGET /api/v1/newses/{id}PATCH /api/v1/newses/{id}→site.save_news
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
title | string · text | |||
slug | string · text | |||
description | string · text | yes | ||
content | object · jsonb | |||
status | string · text | draft, published, archived | ||
published_at | string (date-time) · timestamptz | yes | ||
created_by | string (uuid) · uuid | yes | app_user | |
updated_by | string (uuid) · uuid | yes | app_user | |
meta_title | string · text | yes | ||
meta_description | string · text | yes | ||
view_count | integer · int4 | |||
is_featured | boolean · bool | |||
featured_image | string (uuid) · uuid | yes | file | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
scheduled_at | string (date-time) · timestamptz | yes |
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_redirectsGET /api/v1/page_redirects/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
from_path | string · text | |||
to_path | string · text | |||
status_code | integer · int4 | |||
note | string · text | yes | ||
is_public | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (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
GET /api/v1/pagesGET /api/v1/pages/{id}PATCH /api/v1/pages/{id}→site.update_pageDELETE /api/v1/pages/{id}→site.delete_page
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
title | string · text | |||
slug | string · text | |||
meta_title | string · text | yes | ||
meta_description | string · text | yes | ||
meta_keywords | string · text | yes | ||
status | string · text | draft, published, archived | ||
parent_id | string (uuid) · uuid | yes | page | |
sort_order | integer · int4 | |||
is_homepage | boolean · bool | |||
show_in_header | boolean · bool | |||
show_in_footer | boolean · bool | |||
is_default | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: site_id, slug
Policy
Table policy · Resource policies
GET /api/v1/policiesGET /api/v1/policies/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
policy_type | string · text | cookie_policy, privacy_policy, terms_of_service | ||
title | string · text | |||
content | string · text | |||
is_active | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (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_themesGET /api/v1/site_code_themes/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
name | string · text | |||
manifest | object · jsonb | |||
version | integer · int4 | |||
imported_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (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_contentsGET /api/v1/site_contents/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
key | string · text | |||
kind | string · text | text, rich_text, image, link, hours, list, number, boolean, json | ||
section | string · text | |||
label | string · text | |||
help | string · text | yes | ||
value | object · jsonb | |||
sort_order | integer · int4 | |||
is_public | boolean · bool | |||
updated_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (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_deploysGET /api/v1/site_deploys/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
version | integer · int4 | |||
kind | string · text | static, theme | ||
provider | string · text | cloudflare, local | ||
status | string · text | uploading, live, failed, superseded, removed | ||
url | string · text | yes | ||
host | string · text | yes | ||
script_name | string · text | yes | ||
provider_version_id | string · text | yes | ||
source | string · text | api, cli, desktop, chat, screen, rollback | ||
rollback_of | string (uuid) · uuid | yes | site_deploy | |
file_count | integer · int4 | |||
total_bytes | string (int64) · int8 | |||
has_worker | boolean · bool | |||
manifest | object · jsonb | |||
log | object · jsonb | |||
error | string · text | yes | ||
note | string · text | yes | ||
created_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
finished_at | string (date-time) · timestamptz | yes |
Primary key: id
Unique: site_id, version
SiteInfo
Table site_info · Resource site_infos
GET /api/v1/site_infosGET /api/v1/site_infos/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
company_name | string · text | |||
company_description | string · text | yes | ||
slogan | string · text | yes | ||
founded_year | integer · int4 | yes | ||
footer_text | string · text | yes | ||
footer_copyright | string · text | yes | ||
website_url | string · text | yes | ||
meta_description | string · text | yes | ||
meta_keywords | string[] · text[] | |||
logo_light | string (uuid) · uuid | yes | file | |
logo_dark | string (uuid) · uuid | yes | file | |
favicon | string (uuid) · uuid | yes | file | |
is_public | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (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/sitesGET /api/v1/sites/{id}POST /api/v1/sites→site.create_site
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
type | string · site_type | business, commerce, landing, portal | ||
name | string · text | |||
slug | string · text | |||
locales | string[] · text[] | |||
default_locale | string · text | |||
auth_mode | string · text | |||
published | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: workspace_id, slug
ThemeSnapshot
Table theme_snapshot · Resource theme_snapshots
GET /api/v1/theme_snapshotsGET /api/v1/theme_snapshots/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
theme_id | string (uuid) · uuid | theme | ||
kind | string · text | auto, manual, before | ||
label | string · text | yes | ||
config | object · jsonb | |||
section_count | integer · int4 | |||
created_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz |
Primary key: id
Theme
Table theme · Resource themes
GET /api/v1/themesGET /api/v1/themes/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | site | ||
name | string · text | |||
status | string · text | |||
version | integer · int4 | |||
config | object · jsonb | |||
schema_version | string · text | |||
parent_id | string (uuid) · uuid | yes | theme | |
custom_css | string · text | yes | ||
created_at | string (date-time) · timestamptz | |||
published_at | string (date-time) · timestamptz | yes | ||
updated_at | string (date-time) · timestamptz | |||
scheduled_at | string (date-time) · timestamptz | yes |
Primary key: id
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
site.auth_mode_changed | A site changed its visitor sign-in mode. | site_id, auth_mode |
site.blocks_changed | A page layout was saved into a draft theme. | site_id, theme_id, page_id, blocks |
site.blog_taxonomy_changed | A blog category, tag or author changed. | id, kind |
site.contact_info_changed | The contact details changed. | id, siteId |
site.contact_submitted | Somebody filled in the contact form. inbox opens a conversation on this. | id, siteId, email |
site.content_changed | Editable content of a site changed (a rebuild/revalidate hook can listen to it). | site_id, keys |
site.created | A site was created. | site_id, type, host |
site.deploy_failed | A deploy of a site failed. | site_id, deploy_id, version, error |
site.deploy_removed | A deployed site was taken off the edge. | site_id, deploy_id |
site.deploy_started | A new version of a site started uploading to the edge. | site_id, deploy_id, version |
site.deployed | A version of a site went live on the edge. | site_id, deploy_id, version, url |
site.domain_added | An address was claimed for a site. | domain_id, site_id, host |
site.domain_removed | An address stopped pointing at a site. | domain_id, host |
site.domain_verified | A custom domain passed verification. | domain_id, host |
site.faq_changed | A question, group or ordering changed. | id, kind |
site.homepage_changed | A different page became the home page. | id, siteId |
site.info_changed | The company details changed. | id, siteId |
site.links_shared | Somebody (or Era) asked where the sites can be seen. | count |
site.locale_changed | A site changed its default or available languages. | site_id, default_locale, locales |
site.navigation_changed | The header or footer menu changed. | id, siteId |
site.news_published | A news item changed state. | id, status |
site.news_saved | A news item was written or edited. | id, siteId, slug |
site.page_created | A page was added. | id, siteId, slug |
site.page_deleted | A page was deleted. | id, siteId |
site.page_moved | A page moved in the page tree. | id, parentId |
site.page_status_changed | A page was published, drafted or archived. | id, status |
site.page_updated | A page was edited. | id, slug, redirectedFrom |
site.policy_changed | A legal page changed. | id, siteId, policyType |
site.post_published | A blog post went live. | id, siteId |
site.post_saved | A blog post was written or edited. | id, siteId, slug |
site.post_tagged | A post gained or lost a tag. | postId, tagId |
site.post_unpublished | A blog post came down. | id, status |
site.primary_domain_changed | A different address became the primary one. | domain_id, host |
site.published | A site went live. | site_id |
site.redirect_changed | A redirect was written or removed. | id, siteId, fromPath |
site.reference_deleted | A reference was removed. | id, siteId |
site.reference_saved | A reference was written or edited. | id, siteId |
site.submission_marked | A message was marked read, replied, archived or spam. | id, status |
site.theme_changed | A draft theme was restyled. | site_id, theme_id |
site.theme_draft_created | A draft theme was forked from the live one. | site_id, theme_id, forked_from |
site.theme_imported | A code site’s theme manifest (era.theme.json) was imported. | site_id, version, sections |
site.theme_published | A theme draft was published. | site_id, theme_id |
site.theme_scheduled | A theme draft was scheduled to go live, or its schedule was cancelled. | site_id, theme_id, publish_at |
site.unpublished | A site was taken down. | site_id |