Design, Site Settings & Files

Part of the Content API. Endpoints for the site’s design (template and color scheme), its editable settings, and its uploaded files.

Design

/api/v3/design/

Scopes: design:read / design:write. The site’s design is a template (the design family: miranda, miranda-thin, molly, anna, anna-modern) and a color scheme: a named variation of that template plus its variable values. CSS is generated from these on request, so changes are live immediately.

  • GET /design/ → {"template", "template_family", "styles": {"name", "vars": {...}}, "previous_template"}.
  • GET /design/templates/ → the catalogue, [{"name", "screenshot", "variations": [names]}].
  • GET /design/templates/<template>/variations/<name>/ → that variation’s variables.
  • PATCH /design/ with any of:
    • template: switch template. Unless variation is also given, the new template’s default variation is applied, since variables are specific to a template family.
    • variation: switch color scheme within the (new or current) template.
    • vars: override individual variables of the resulting scheme, e.g. {"palette-brand-color": "#0066A7"}. Unknown variable names are a 422 listing the available ones. Values are plain CSS values (up to 512 characters, with a small set of forbidden characters, and no url(), image-set() or expression(): colors, sizes and font names only); for the SCSS-compiled templates (miranda, miranda-thin, molly) they are compiled before saving, so a value that would break the site’s stylesheet is a 422.
  • POST /design/preview/ takes the same body, validates it, and returns preview_url: the site rendered with that design but nothing saved. It needs only design:read. Open the link while logged in to the site’s admin: the unsaved variables are only applied for a logged-in admin.

GET /design/ also reports consistent: whether the stored color scheme sets the variables the current template needs. The scheme’s name doesn’t matter (the design editor names any scheme you have edited custom), and extra variables are ignored. anna and anna-modern need every one of their variables; miranda, miranda-thin and molly have a default for each, so they only need the scheme to have at least one of theirs rather than another template’s. The API never writes an inconsistent scheme, and any PATCH on a site that has one fills in the missing variables first, from the variation of the same name or the template’s default one, keeping the site’s own values (problem says what was wrong).

Switching away from a legacy template records previous_template so the admin’s revert still works. Changes appear in the site’s settings history and can be undone: GET /design/revisions/ lists them, and POST /design/revisions/<rev>/revert/ puts the template and color scheme back (see Revisions and undo). Some content features depend on the template (for example tiles and featured agents on anna-modern); the admin hides them for other designs.

From the command line: geekcli design templates, geekcli design preview --snapshot preview.png, geekcli design set with geekcli (see the geekcli guide).

Fonts

On anna and anna-modern the fonts are two site settings, not part of the color scheme: HEADING_FONT (headings and the home page headline) and BODY_FONT (body text, navigation and buttons). Send them to PATCH /settings/ with the settings:write scope:

{"HEADING_FONT": "Playfair Display",
 "BODY_FONT": "Raleway"}

Both are choice settings, and GET /settings/HEADING_FONT/ lists the families in choices: Arapey, Cormorant Garamond, Crimson Text, DM Sans, EB Garamond, Figtree, Fraunces, Inter, Italiana, Josefin Sans, Jost, Karla, Lato, Libre Baskerville, Lora, Manrope, Merriweather, Montserrat, Mulish, Nunito Sans, Open Sans, Outfit, Playfair Display, Poppins, PT Sans, Raleway, Roboto, Source Sans 3, Urbanist and Work Sans. null clears one: a blank BODY_FONT is the theme’s own font (Lato), and a blank HEADING_FONT means headings use the body font. On another template the change is a 422 naming TEMPLATE.

The fonts are served from the site itself and preloaded with the page, with a fallback sized to match so text does not move when the font arrives. Use these settings for any font change instead of a Google Fonts @import, <link> or @font-face in a footer, sidebar or page, which delays the first paint or shifts the layout and lowers the site’s PageSpeed score.

From the command line: geekcli settings get HEADING_FONT, geekcli settings set HEADING_FONT="Playfair Display" BODY_FONT=Raleway with geekcli.

Site settings

/api/v3/settings/

Scopes: settings:read / settings:write. The settings a site owner can change in the admin (Website Settings), with JSON-native values. Anything else in the site’s configuration is not readable or writable here.

  • GET /settings/ returns every editable setting; GET /settings/<NAME>/ one.
  • PATCH /settings/ with {"NAME": value, ...} changes only the settings you send. null (or "") removes the site’s own value so the setting falls back to the inherited default; required settings cannot be cleared. The response lists the settings you sent, updated.

Each setting looks like:

{"name": "LEAD_CAPTURE_ON_PROPERTY",
 "label": "Lead Capture On Property",
 "group": "Advanced Site Settings",
 "description": "...",
 "type": "choice",
 "choices": [0, 1, 2, 3, 4, 5, 10],
 "value": 1,
 "overridden": false,
 "inherited_value": 1,
 "required": false,
 "depends": {}}

type is one of string, email, color, file (a site file path), boolean, integer, integer_list, list (of strings), object, yaml, choice. depends names other settings whose values must allow this one (as the admin form does); the API rejects a change that violates it. Changes are recorded in the site’s settings history and take effect on the live site within a few seconds.

Undo: GET /settings/revisions/ lists the changes made to these settings, GET /settings/revisions/<rev>/ previews a revert, and POST /settings/revisions/<rev>/revert/ undoes that change and every later one, returning the settings it changed. See Revisions and undo.

From the command line: geekcli settings list, geekcli settings set NAME=value with geekcli (see the geekcli guide).

Files

/api/v3/files/

Scopes: files:read / files:write. The site’s uploaded files (what the admin’s Manage Files page shows), served from https://u.realgeeks.media/. Paths are relative to the site’s folder, e.g. images/logo.png; the root is "".

  • GET /files/?path=<folder> lists a folder’s entries (folders first). ?q=<text> searches names across all folders. Pages hold up to page_size (default 100, max 500) entries; follow next_cursor with ?cursor= when present.
  • GET /files/<path>/ returns one entry; DELETE /files/<path>/ deletes a file, or a folder and everything in it (for 90 days it can be restored). If storage refuses some objects the call is a 502 and those entries stay listed for a retry.
  • POST /files/upload/ uploads a file, either as multipart/form-data (file, plus path, optional name, overwrite) or as JSON {"path", "name", "content_type", "content_base64", "overwrite"?}. JSON may send content_url instead of content_base64: the server downloads the file itself, over https only and from an allow-list of hosts (the AI platforms’ file storage, including ChatGPT’s, Wikimedia Commons upload.wikimedia.org, Flickr *.staticflickr.com and u.realgeeks.media); any other host is a 422 naming the allowed ones. Redirects are followed for at most three hops, each checked against the allow-list again; the host must resolve only to public addresses; a download stops at the size limit and times out after 15 seconds. Max 8,000,000 bytes (JSON base64 uploads are limited to about 2.5 MB of body; use multipart or content_url for larger files). Allowed types, by extension: jpg/jpeg, png, gif, ico, mp4, pdf, txt, css. Files are served inline from a domain shared by every site, so HTML, SVG, XML and scripts cannot be uploaded here (use the admin). The stored content_type follows the extension; a content_type that contradicts it is a 422. Whitespace and slashes in names become _. An existing name is a 409 unless overwrite is true; a folder of that name is always a 409.
  • POST /files/folders/ with {"path", "name"} creates a folder.
  • POST /files/move/ with {"from", "to", "overwrite"?} moves or renames a file (folders cannot be moved).

An entry:

{"name": "logo.png",
 "path": "images/logo.png",
 "type": "file",
 "url": "https://u.realgeeks.media/.../logo.png",
 "size": 12345,
 "content_type": "image/png",
 "last_modified":
   "2026-09-02T18:00:00+00:00",
 "dimensions": {"width": 400,
                "height": 200},
 "thumbnail_url":
   "https://t.realgeeks.media/resize/140x/..."}

Use an entry’s url wherever a file is referenced: facebook_image on posts, image src in HTML, and file-typed site settings such as HEADER_LOGO and FAVICON.

Restoring files

GET/api/v3/files/versions/?path=
GET/api/v3/files/deleted/?path=
POST/api/v3/files/restore/

A delete or an overwrite from the last 90 days can be reversed, including ones made on the admin’s Manage Files page. What counts is when the file was deleted or replaced, not when it was first uploaded. Older deletes and overwrites are not listed and cannot be restored.

  • GET /files/versions/?path=<file> lists the file’s current state and the versions replaced or deleted in that time, newest first: {"version_id", "at", "action": "saved|deleted", "size", "current", "restorable"}.
  • GET /files/deleted/?path=<folder> lists the files and folders deleted under a folder, at any depth: {"path", "type", "deleted_at"}. Follow next_cursor with ?cursor= when present.
  • POST /files/restore/ with {"path", "version_id"?} brings a file back at the same URL: the version you name, or without version_id the one before the current state, which is the file as it was before it was deleted or replaced. Given the path of a deleted folder, it restores the folder and what was deleted with it, but not files removed from it earlier. The response is {"restored": [entries], "incomplete"}; incomplete means a very large folder was only partly restored, so call it again for the folders still listed by /files/deleted/.

A restore adds a new version and never removes one, so it can be undone the same way. A moved file is undone by moving it back. 409 conflict means the delete or overwrite is too old, or there is nothing earlier to restore; 503 file_history_unavailable means version history cannot be read for the site.

From the command line: geekcli files upload logo.png --to images, geekcli files upload --from-url <url> with geekcli (see the geekcli guide).