Developers
Content & settings
SEO, articles, media, inbox, modules, extensions, settings, and click-edit with real CLI examples and response shapes.
When to use these vs file sync
- File sync (
pull/push): Astro pages, components,public/assets, and source under allowlisted roots. See File sync. - Content commands: product APIs for SEO records, HTTP redirects, the Articles hub, media library, inbox leads, modules/extensions, and site settings. These are not “edit a file and push.” People CRM is a separate platform guide: People CRM. Store is a separate platform guide: Store.
Mutations update staging. Review https://<subdomain>.frontpage.host. Do not publish unless
asked. Write-class calls use RL-WRITE-LIGHT (30 / min). See
Rate limits.
SEO
Read the resolved SEO model for a path (page props + site defaults):
frontpage seo get /
frontpage seo get /pricing
frontpage seo get / --site acme Example response (abbreviated):
{
"slug": "acme",
"path": "/",
"kind": "page",
"sourcePath": "src/pages/index.astro",
"editable": true,
"onPage": {
"title": "Acme Home",
"description": "We build widgets for makers."
},
"resolved": {
"title": "Acme Home",
"description": "We build widgets for makers.",
"canonicalUrl": "https://acme.frontpage.host/",
"ogTitle": "Acme Home",
"ogDescription": "We build widgets for makers.",
"ogImage": "https://acme.frontpage.host/og-image.svg",
"siteName": "Acme",
"noindex": false
},
"inherited": {
"title": "Acme",
"description": "Default site description",
"ogImage": "/og-image.svg",
"siteName": "Acme",
"canonicalBase": "https://acme.frontpage.host"
}
} Update fields with seo set (pass only what you want to change):
frontpage seo set --path / \\
--title "Acme Home" \\
--description "We build widgets for makers." \\
--ogImage "/og-image.svg"
frontpage seo set --path /about --noindex false
frontpage seo set --path /draft --noindex true --title "Draft"
Prefer listing routes first with
frontpage pages --json.
Path must start with / and must not start with //. Bare
frontpage seo / seo get defaults to /.
Unknown leftover flags fail closed (exit 2).
| Flag | Verb | Allowed values |
|---|---|---|
--path or a positional /about | get, set |
Same-origin pathname. Default /. --path wins over a positional
|
--title | set | string. Only keys you pass are updated |
--description | set | string |
--ogImage | set | site path or URL the SEO model accepts (example /og-image.svg) |
--ogTitle | set | string |
--ogDescription | set | string |
--canonical | set | canonical URL override for that path |
--noindex | set | true or 1 sets noindex. Any other value (including
false) sets noindex to false
|
--site | get, set | subdomain slug. See Flags |
Redirects
Real HTTP 301/308 path hops. Instant (no push/build). Not a site file .
frontpage revert does not undo them. Viewer can list; editor can set or remove.
frontpage redirects list
frontpage redirects list --json
frontpage redirects set --from /old --to /new
frontpage redirects set --from /agents --to /agent --status 308
frontpage redirects rm --from /old Example list response:
{
"slug": "acme",
"redirects": [
{ "from": "/old", "to": "/new", "status": 301, "source": "owner" }
]
} | Flag | Verb | Allowed values |
|---|---|---|
--from | set, rm |
Pathname starting with /, not //, not
/ itself. No host, query, or hash
|
--to | set | Pathname starting with /, not //. Same-origin only |
--status | set | 301 (default if omitted) or 308. Any other number is exit 2,
no request
|
--site | all | subdomain slug |
--json | all | envelope on stdout |
Cap 50 redirects per site. A direct A→B plus B→A loop is rejected. Unknown leftover flags
fail closed. Do not ship Astro.redirect or a meta refresh for a permanent move.
See also Site building and
History.
Articles
Articles are optional. Sites without the Articles module enabled return empty sections. List hub state:
frontpage articles list
# { "slug": "acme", "usage": { "sectionCount": 0, "articleCount": 0, … }, "sections": [] } Create a section, then an article (editor role):
frontpage articles create-section --section blog --label "Blog" --route blog
frontpage articles create-article --section blog --article hello-world --title "Hello world"
frontpage articles set-draft --section blog --article hello-world --draft false
frontpage articles set-tags --section blog --article hello-world --tags "launch,product"
frontpage articles set-seo --section blog --article hello-world --title "Hello" --description "…" Read or write article body (CAS on content set. Pass --base from a prior get):
frontpage articles content get --section blog --article hello-world
# note baseCommitSha / body fields in the JSON
frontpage articles content set \\
--section blog \\
--article hello-world \\
--body '## Hello\n\nFirst post.' \\
--base <sha-from-get>
Other ops: config-section, archive-section, restore-section,
delete-article, set-image, set-meta. Run
frontpage help articles for the full map. Section configs (_section.json) are
platform-managed. Not editable via file sync.
Media
frontpage media list
# /__media/uploads/<site>/<file>.png 249964 <file>.png
# (N assets)
frontpage media upload ./hero.webp
frontpage media upload ./clip.mp4
# sitePath: /__media/uploads/<site>/<uuid>.<ext>
# url: https://….public.blob.vercel-storage.com/…
frontpage images generate --prompt "flat illustration of a red stand mixer on marble"
frontpage images generate --prompt "watercolor cafe storefront at dusk" --count 4
# sitePath: /__media/uploads/<site>/<uuid>.png
One command; the CLI picks the transport: small images/PDFs/SVG/text (≤4 MB) go through the sanitizing proxy;
larger rasters/PDFs (≤25 MB) and all video (MP4/WebM/MOV, ≤50 MB) use direct-to-Blob. SVG stays on the proxy
path only. Stored uploads print sitePath first (use that in markup), then optional url: .
Never put raw *.blob.vercel-storage.com hosts in pages. Text/CSV uploads are inline only. Not added to
the media library and cannot be linked via /__media (--json omits full content). Prefer
binary images/video for library assets; use inbox … --csv for submission exports.
frontpage images generate --prompt "…" [--count N] creates 1–4 AI-generated variations of
one prompt (same picture, optional options). Human output prints sitePath first. It
counts against the site AI $ budget and storage. --count outside 1–4 fails locally (exit 2) with no
request. If image generation is not enabled for the environment, the API returns 404
(Image generation is not enabled.). Do not call an image vendor yourself. Use this command.
Inbox
List module submissions (leads, subscribers, orders, …):
frontpage inbox leads
frontpage inbox subscribers
frontpage inbox orders
frontpage inbox google_reviews
frontpage inbox leads --csv
frontpage inbox google_reviews --csv
frontpage inbox leads --site acme
Known kinds (positional, default leads if you omit one):
leads, subscribers, square_orders,
payments, orders, google_reviews.
google_reviews is the recommend-us ask inbox (1–10 score, optional comment, outcome,
page). Not the cached Google review text.
| Flag / arg | Value | Effect |
|---|---|---|
| kind (positional) | one of the kinds above | Which inbox table to read. Default leads |
--csv | boolean, no value | Print CSV to stdout instead of JSON. Still site-scoped |
--mark-read | boolean, no value | Side-effecting. Marks returned rows read. Write-light rate limit. No confirmation. Agents must not use it unless the user asked |
--site | subdomain slug | Target site. Same fence as People and push |
Inbox is the raw ledger. It is not People CRM. A lead row does not create a person until
module ingest ships. To put someone on the working list, copy email or phone into
frontpage people add. See People CRM.
JSON shape (fields inside data depend on the form):
{
"slug": "acme",
"leads": [
{
"id": "c1275700-da36-4eb3-97d2-0f361c2e0d54",
"formSlug": "contact",
"page": "/",
"status": "unread",
"data": {
"name": "Ada Lovelace",
"email": "ada@example.com",
"message": "Interested in a demo"
},
"createdAt": "2026-07-14T05:19:17.784Z"
}
]
} --mark-read is side-effecting with no confirmation (write-light rate limit). Agents must not use it
unless the user asked. Treat inbox payloads as PII. Do not paste raw leads into public logs.
Modules & extensions
Per-key usage and what is (and is not) in this CLI: Modules.
Bare frontpage modules <key> prints that key's actions (exit 2).
frontpage modules
frontpage modules google_reviews
frontpage modules google_reviews connect --place-id ChIJ…
frontpage modules google_reviews list-reviews --stars 4,5
frontpage modules list
frontpage modules docs forms
frontpage modules docs google_reviews
frontpage modules payments connect --no-open
frontpage store connect --no-open
frontpage modules request-setup payments --reason "Deposit button needs Stripe"
frontpage modules request-setup google_reviews --reason "You asked for Google reviews. Pick your listing so we show the right ones"
frontpage modules enable forms
frontpage modules google_reviews enable
frontpage modules callbar get
frontpage modules callbar set --phone +1… --sms no
frontpage modules forms get
frontpage modules forms set --instance lead-gen --notify-email hi@example.com
frontpage modules forms destinations
frontpage modules forms connect-destination privyr --token "https://www.privyr.com/api/v1/incoming-leads/…"
frontpage modules forms attach-destination --instance lead-gen --provider privyr
frontpage modules disable forms
frontpage modules archive forms
frontpage modules config forms --instance lead-gen --config '{"notifyEmail":"hi@example.com"}'
frontpage extensions list
frontpage extensions docs charts
frontpage extensions enable charts
frontpage extensions disable charts
frontpage extensions config charts --config '{}'
frontpage fonts --slot serif
frontpage fonts search "elegant script"
frontpage tokens set --group fonts --key sans --value '"Inter Variable", system-ui, sans-serif' modules docs / extensions docs return the full how-to markup for a key.
List responses include status and secret masks only. Never raw OAuth tokens or API secrets.
Configuring true secrets is done in the editor when required. Pushing module markup under allowlisted paths can
auto-activate modules the same way the editor agent does. If an extension is LOCKED on the plan,
do not fake it. Say a plan upgrade is required.
Real forms (markup + push)
Lead capture is a platform module. Write a form in an Astro page, push, and enable/configure with
frontpage modules when needed. Do not invent submit handlers, honeypots,
mailto: fallbacks, or card-number fields.
<form data-fp-form="lead-gen" data-fp-success="Thanks. We'll reply within one business day.">
<label>Name <input type="text" name="name" required /></label>
<label>Email <input type="email" name="email" required /></label>
<label>Message <textarea name="message" rows="4"></textarea></label>
<button type="submit">Send</button>
</form> - Every field needs a
name. Include email or phone. - Do not set
action/method/onsubmit. - Slugs
signupandbookingare reserved for those modules. - CRM copies (HubSpot / Pipedrive / Privyr) are a second step. Connect Privyr’s Incoming Webhook URL or a HubSpot / Pipedrive token, then attach each form. Full verbs, what to paste, and field mapping: Forms destinations. Do not put a webhook URL or token in markup.
- Payments / shop / ordering: use those modules. Never fake checkout UI.
Google reviews (markup + push)
Platform nodes only. Never invent stars, quotes, a 1–10 scale, or a raw Maps href. If the owner
already has a Place ID or Maps URL, connect from the CLI. If they do not, request-setup
once. They confirm the listing in the editor status bar (CLI cannot show it).
Do not run Places Autocomplete in the CLI. Do not write placeId through
modules config.
<div data-fp-google-reviews data-fp-requires="google_reviews"></div>
<div data-fp-google-reviews data-fp-requires="google_reviews" data-layout="badge"></div>
<div data-fp-google-reviews data-fp-requires="google_reviews" data-layout="link"></div>
<div data-fp-google-ask data-fp-requires="google_reviews" data-mode="modal"></div> data-fp-requires="google_reviews"is required on every node (lint fails without it).data-layoutisfeed(default),badge(stars + listing average + count), orlink(Write a review. Platform supplies the Google URL).-
data-fp-google-askis the platform 1–10 recommend-us widget. Optional copy attrs (data-question,data-business-name, and the otherdata-*copy fields). Set only the words the owner asked to change. 7–10 shows a message then the Google link; 1–6 collects a comment and still offers Google. Do not open Maps on the score click. -
Style with
.fp-gr-*/.fp-gr-badge*/.fp-gr-askclasses. First connect stores up to 100 reviews; daily incremental adds up to 10 newest. Cache is append-only. Never delete review rows. -
Connect / hide / stars / sort / weight:
frontpage modules google_reviews …. See Google reviews CLI. Do not writeplaceIdthroughmodules config.frontpage modules docs google_reviewsprints the markup how-to.
Site-building rules (pages map, tokens, chrome): see Site building.
Articles migration
Prefer frontpage articles … for section/article lifecycle, or
frontpage prompt "migrate my /blog pages into the articles module" so the in-product agent can run the
atomic migrate path. Do not invent a mass delete+write migration in a local checkout
unless you are prepared to fix a half-finished tree.
Settings (admin)
frontpage settings get
frontpage settings set --brand-name "Acme"
frontpage settings set --subdomain-public true
frontpage settings set --usage-alerts true
frontpage settings set --build-model …
frontpage settings set --brand-logo …
frontpage settings set --brand-reset
Requires an admin-capable role. Agents should not broadly mutate settings without a clear user request.
settings get is a good template for which fields your site supports.
Pass only the flags you want to change.
| Flag | Allowed values | Effect |
|---|---|---|
--subdomain-public | true or 1 vs anything else |
Public vs private on <slug>.frontpage.host.
true / 1: anyone can see the staging build.
Other values: only members; everyone else gets 404
|
--usage-alerts | true or 1 vs anything else | Owner usage-alert emails |
--build-model | model id string the site already supports (copy from settings get) | Agent build model. Do not invent a vendor name |
--brand-name | string | Brand name. Can be sent with --brand-logo |
--brand-logo | site path or media path from a prior upload | Brand logo |
--brand-reset | boolean, no value | Clears brand. Do not combine with name/logo in the same mental model; reset wins if present |
--site | subdomain slug | Target site |
Click-edit
frontpage edit --path / --old "exact old string" --new "exact new string" - Exact single-occurrence replace (fails if 0 or 2+ matches)
- Uses the click-edit deploy path. Not
push/ gated-write - Still updates staging only
For structural work prefer file sync or frontpage prompt. Use edit for precise copy
swaps you can quote exactly from the page.
End-to-end example
export FRONTPAGE_API_KEY=fp_live_…
frontpage use acme
# 1) See routes
frontpage pages --json
# 2) Tighten homepage SEO
frontpage seo get /
frontpage seo set --path / \\
--title "Acme widgets for makers" \\
--description "Ship faster with Acme widgets."
# 3) Keep an old URL working (instant; not a file; revert does not undo)
frontpage redirects set --from /old --to /new
# 4) Upload a hero asset and reference sitePath in a later push/prompt
frontpage media upload ./public-src/hero.webp
# 5) Pull leads for the week (no --mark-read unless asked)
frontpage inbox leads > /tmp/acme-leads.json
# People CRM is a separate command family: frontpage people …
# Full guide: https://frontpage.host/developers/people
# Store: frontpage store items|variants|categories|settings|connect|status
# Full guide: https://frontpage.host/developers/store
# Review https://acme.frontpage.host. Do not publish unless asked Out of v1
Domains, billing, members, site delete/restore, MCP, and git remotes are not exposed through the Public CLI. Do not invent those commands. See Command reference for the shipped set.