Developers
Modules
Frontpage CLI module commands: list, docs, request-setup, enable, disable, archive, and config, plus form destinations, Google reviews, and Stripe OAuth.
Grammar
frontpage modules <key> # that key's CLI usage (exit 2)
frontpage modules <key> <action> [flags]
frontpage modules list|docs|request-setup|enable|disable|archive|config …
Key-first and verb-first both work: frontpage modules google_reviews enable is the same as
frontpage modules enable google_reviews. Bare frontpage modules <key> prints that
key's actions, not the markup how-to. frontpage modules docs <key> stays the agent markup
guide.
JSON: add --json. Usage still exits 2. Secrets are never returned (masks only).
See also Content & settings.
Generic actions (every listed key)
| Action | What it does |
|---|---|
docs | Print markup / agent how-to for the key |
request-setup | Amber bar in the editor (CLI cannot show it). Use when the owner must finish connect there. |
enable / disable / archive | Turn the module on, off, or archive it |
config --config '{…}' | Patch whitelisted JSON. Do not write placeId this way. |
Keys
Jump to a key for its extra actions. Other keys stay generic-only until a later phase.
| Key | CLI today | Not in this CLI yet |
|---|---|---|
callbar | Generic + get / set --phone | None |
forms | Generic + get / set --instance / destinations | Do not target default. Token write is admin. |
booking | Generic + get / set --notify-email | None |
signups | Generic + get / set --notify-email | Confirmations: paid plan |
payments | Generic + status / connect [--no-open] / disconnect | OAuth finishes in the browser. Shares Stripe with shop. |
shop | Generic + status / connect [--no-open] / disconnect | Same Stripe account as payments. Shipping step stays in Modules. |
ordering | Generic + status / connect [--no-open] / sync / disconnect | Fulfillment get/set not in this CLI yet |
articles | modules docs articles; content lives on frontpage articles … | Do not fold article create/edit under modules articles |
google_reviews | Generic + connect / resolve / status / list-reviews / hide / show / set-stars / set-sort / set-weight / reset-weights / write-review-url / refresh / disconnect | Autocomplete (hub only). Do not write placeId via config. |
If an action is listed as “not in this CLI yet,” use Modules or the editor. Not
frontpage prompt as a substitute. Ask-widget scores: frontpage inbox google_reviews.
Examples
frontpage modules google_reviews
frontpage modules google_reviews connect --place-id ChIJ…
frontpage modules google_reviews list-reviews --stars 4,5
frontpage modules google_reviews docs
frontpage modules google_reviews enable
frontpage modules google_reviews request-setup --reason "You asked for Google reviews. Pick your listing so we show the right ones"
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 ordering connect --no-open
frontpage modules ordering status
frontpage modules payments connect --no-open
frontpage modules docs forms
frontpage modules enable forms
frontpage modules config callbar --config '{"phone":"+1…"}'
frontpage modules payments request-setup --reason "Deposit button needs Stripe"
frontpage articles list GET connection summary
GET /api/v1/sites/:slug/modules/:key may include connection: { kind, ready, display }
when the module has an external prerequisite (Stripe, Square, or a Google listing). Display is masks and public
listing fields only. No tokens, no Stripe account ids, no review bodies.
callbar
Typed get / set write the same whitelist as
frontpage modules config callbar --config '{…}'
(phone, sms, label).
sms is yes or no.
frontpage modules callbar
frontpage modules callbar get [--site <slug>] [--json]
frontpage modules callbar set --phone +1… [--sms yes|no] [--label "Call"] | CLI | What it does |
|---|---|
get | Print phone, sms, label. Human lines, not a JSON dump. Add --json for agents. |
set --phone +1… [--sms yes|no] [--label "Call"] | POST action=set. Same sanitizeConfigPatch as the hub. |
Generic docs, enable / disable / archive, and
config --config still work.
This CLI will not invent a second call bar (markup is data-fp-callbar in files or the editor agent) or accept unknown leftover flags (exit 2).
frontpage modules callbar get
frontpage modules callbar set --phone +14085551234 --sms no
frontpage modules callbar set --label "Call us"
frontpage modules config callbar --config '{"phone":"+14085551234"}' forms
Forms are per-instance. get without --instance lists each form.
set requires --instance. Do not target default.
Destinations are an opt-in CRM copy of those same submissions. Not a new module, not a replacement
for Frontpage storage or the notify email.
frontpage modules forms
frontpage modules forms get [--instance <slug>]
frontpage modules forms set --instance <slug> [--notify-email] [--confirm yes|no]
frontpage modules forms destinations [--instance <slug>]
frontpage modules forms connect-destination <privyr|hubspot|pipedrive> --token "…" [--lead-source "…"]
frontpage modules forms attach-destination --instance <slug> --provider <id>
frontpage modules forms detach-destination --instance <slug> --provider <id>
frontpage modules forms remove-destination <id> --yes
frontpage modules forms request-destination-setup <id> --reason "…"
Bare frontpage modules forms prints this key's CLI usage (exit 2).
frontpage modules docs forms stays the markup / agent how-to.
JSON: add --json. Secrets are never returned (last4 masks only).
Instances
| CLI | API | What it does |
|---|---|---|
get | GET …/modules/forms | List instances (id, status, notifyEmail) plus destination masks. With --instance, print that form. |
set --instance <slug> [--notify-email] [--confirm yes|no] | POST action=set | Same whitelist as the hub (notifyEmail, confirmEnabled). One inbox per form. |
Confirmation emails match Modules: paid plan required; turning them off is always allowed.
Generic config --config still works for those two fields. Do not write
destinations, tokens, or webhook URLs through config. Those keys are rejected.
frontpage modules forms get
frontpage modules forms get --instance lead-gen
frontpage modules forms set --instance lead-gen --notify-email hi@example.com
frontpage modules forms set --instance lead-gen --confirm no
frontpage modules config forms --instance lead-gen --config '{"notifyEmail":"hi@example.com"}' Destinations (CRM copies)
Two layers. Connect a CRM once per site (store the secret). Then attach
each form that should also send a copy. Connecting does not attach every form.
Frontpage still stores the lead and still emails notifyEmail. Removing a destination
never deletes the Leads inbox.
v1 providers: privyr, hubspot, pipedrive.
One secret per (site, provider). A second call to connect-destination replaces
it. Write needs an admin API key.
request-destination-setup is editor. It only lights the amber bar in the
Frontpage editor so the owner can paste there. frontpage modules request-setup forms
stays a no-op (forms are auto-active).
What to paste
Privyr is a URL. HubSpot and Pipedrive are tokens. The CLI flag is still
--token for all three. For Privyr the value is the Incoming Webhook URL, not a
separate key. Do not invent an OAuth dance or a retired API key. Do not put any of this in markup.
| Provider | What to copy | Where it is |
|---|---|---|
privyr | The full Privyr Webhook URL |
Privyr web: Automations → Lead Sources → search Webhooks →
open Incoming Webhook → copy the box labeled Privyr Webhook URL
(starts with https://www.privyr.com/api/v1/incoming-leads/).
There is no Authentication Token on that page. The URL is the secret.
Do not use Outgoing Webhooks (Lead Automations). Do not paste a URL from any other host.
Official: webhooks help.
|
hubspot | Bearer token (service key or private-app access token) with crm.objects.contacts.write |
You need Super admin or Developer tools access in HubSpot.
Navigate to Development → Keys → Service keys → Create service key.
Add new scope. Search for crm.objects.contacts.write and turn only that on.
Create the key → Show → copy it.
Or: Development → Legacy apps → Create legacy app → Private → Scopes → Add new scope →
crm.objects.contacts.write → create the app → Auth → Show token.
Do not use the retired hapikey.
Official:
service keys,
legacy private apps.
|
pipedrive | Personal API token | Profile (top right) → Company settings → Personal preferences → API. Token is per user per company. If you do not see API, a company admin must enable use API on that user’s permission set: Settings → Manage users → Permission sets. Official: find the API token, enabling API. |
CLI verbs
| CLI | API | Role | What it does |
|---|---|---|---|
destinations [--instance <slug>] | GET …/modules/forms → destinations | Editor |
Connected providers (last4 only) and per-form attach state, including last send error.
Human output prints (no destinations) when none are connected.
Token never printed.
|
connect-destination <id> --token "…" [--lead-source "…"] | POST action=destination_connect | Admin |
Store the secret. For Privyr, --token is the Incoming Webhook URL. Second call
replaces it. Optional --lead-source labels the copy in Privyr (HubSpot / Pipedrive
ignore it). Default lead source is
{subdomain} website. {form label}.
|
attach-destination --instance <slug> --provider <id> | POST action=destination_attach | Admin |
This form also sends a copy. Connect first (409 if the secret is missing).
Cannot attach default, booking, or signup.
|
detach-destination --instance <slug> --provider <id> | POST action=destination_detach | Admin | Stop the copy for that form only. Token stays. Other attached forms keep sending. |
remove-destination <id> --yes | POST action=destination_remove | Admin |
Delete the secret and every form copy for that provider.
disconnect-destination is the same verb.
No --yes → exit 2 with the confirm sentence. Leads stay in Frontpage.
|
request-destination-setup <id> --reason "…" | POST action=destination_request_setup | Editor |
Lights the amber bar in the Frontpage editor so the owner can paste.
Does not change form status. Does not store a secret.
request-setup forms stays a no-op.
|
GET /api/v1/sites/:slug/modules/forms includes
destinations: { providers, byInstance }.
Providers list every catalog CRM (connected or not) with last4, optional
leadSource, and a pending request if the amber bar is waiting.
byInstance is per-form attach rows: enabled, lastOkAt,
lastFailedAt, lastError. No tokens, no request bodies.
What Frontpage sends
After the lead is stored and the owner email is queued, Frontpage POSTs each enabled attachment.
A CRM failure never drops the Frontpage lead. Retries are inside that send (4 attempts, 1s / 2s / 4s
backoff, 5s per attempt). 400 / 401 / 403 fail immediately. 429 and 5xx retry.
Last error (≤240 chars, no token, no PII) shows on that form, on the Destinations card, and on
frontpage modules forms destinations. The next 2xx clears it. Frontpage does not email
the owner for a CRM failure.
| Provider | Send | Mapped fields |
|---|---|---|
| Privyr | POST the owner’s allowlisted
https://www.privyr.com/api/v1/incoming-leads/{a}/{b}
(no X-TOKEN header)
| name (required; fallback Website visitor),
source,
email / phone when present,
remaining fields as other_fields.
|
| HubSpot | POST https://api.hubapi.com/crm/v3/objects/contacts with Authorization: Bearer |
Contact properties only:
email, firstname, lastname, phone,
company, website, address when present.
Arbitrary extra keys are not posted (HubSpot 400s unknown properties).
|
| Pipedrive | POST https://api.pipedrive.com/api/v2/persons with x-api-token (not a query string) | name (fallback Website visitor),
primary emails / phones when present.
Custom field hashes are not posted in v1.
|
Name is taken from name / full_name / contact_name, or
first_name + last_name. Phone from phone / tel /
mobile and similar. Booking and signup forms never send a copy (reserved slugs).
A form the owner turned off is skipped.
This CLI will not:
- Invent form markup, a honeypot, or an
action=/ webhook URL in HTML. -
Accept an arbitrary webhook host (SSRF). Privyr is the exception: only
https://www.privyr.com/api/v1/incoming-leads/{id}/{id}. HubSpot and Pipedrive send URLs stay code constants. - Print or log the secret. GET bodies,
--json, and audit payloads stay masks only. - Set a comma-separated notify list (one address per form).
- Target
default,booking, orsignupfor a destination. - Use
request-setup formsas a stand-in for a CRM secret. - Attach every form when you connect. Attach is a second, explicit step.
frontpage modules forms destinations
frontpage modules forms connect-destination privyr --token "https://www.privyr.com/api/v1/incoming-leads/…"
frontpage modules forms connect-destination hubspot --token "…"
frontpage modules forms connect-destination pipedrive --token "…" --lead-source "Acme website"
frontpage modules forms attach-destination --instance lead-gen --provider privyr
frontpage modules forms detach-destination --instance lead-gen --provider privyr
frontpage modules forms remove-destination privyr --yes
frontpage modules forms request-destination-setup hubspot --reason "You asked to send leads to HubSpot"
frontpage modules forms docs
Human connect text: Connected privyr ••••7k2p.
Attach: lead-gen will also send a copy to privyr.
Remove: Removed privyr. Leads still land in Frontpage.
Markup reminder
<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>Phone <input type="tel" name="phone" /></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 signup and booking are reserved.
Full markup how-to: frontpage modules docs forms.
booking
Typed get / set write the same whitelist as generic
modules config (notifyEmail, hours).
frontpage modules booking
frontpage modules booking get [--site <slug>] [--json]
frontpage modules booking set [--notify-email] [--hours "…"] | CLI | What it does |
|---|---|
get | Print notify and hours. |
set [--notify-email] [--hours "…"] | POST action=set. Same sanitizer as the hub. |
Generic docs, enable/disable/archive, and config --config still work.
This CLI will not invent booking markup (data-fp-form="booking" still goes in files or chat)
or confirm a booking request (that stays email / inbox).
frontpage modules booking get
frontpage modules booking set --notify-email book@example.com --hours "Mon–Fri 9–5" signups
Typed get / set write the same whitelist as generic
modules config (notifyEmail, confirmEnabled).
Confirmation emails match Modules: paid plan required.
frontpage modules signups
frontpage modules signups get [--site <slug>] [--json]
frontpage modules signups set [--notify-email] [--confirm yes|no] | CLI | What it does |
|---|---|
get | Print notify and confirm (with paid-plan honesty when relevant). |
set [--notify-email] [--confirm yes|no] | POST action=set. Turning confirmations on on a free plan is 402. |
Generic docs, enable/disable/archive, and config --config still work.
This CLI will not send newsletters (signups is capture-only) or invent signup markup
(data-fp-form="signup").
frontpage modules signups get
frontpage modules signups set --notify-email hello@example.com --confirm no payments
Stripe cannot finish in the terminal. connect needs an admin API key.
It opens Frontpage (you must be logged in there), then Stripe. Already-connected sites stay live .
Disconnect first if you need a different account.
Payments and the online store share one Stripe Express account.
Disconnecting payments fails if the store still uses that account.
request-setup still lights the editor amber bar when you want the owner to finish there.
frontpage modules payments
frontpage modules payments status [--site <slug>] [--json]
frontpage modules payments connect [--no-open]
frontpage modules payments disconnect [--yes] | CLI | API | What it does |
|---|---|---|
status | GET …/modules/payments | Connected?, charges enabled, shared-account note, owner connectUrl. Masks only. No account ids. |
connect [--no-open] | POST action=connect | Returns the Frontpage start URL (not a raw Stripe link). Opens the browser unless --no-open. No-ops if already connected. |
disconnect [--yes] | POST action=disconnect | Same row teardown as the hub, but 409s if the store still uses this account. Past payments are kept. |
Generic docs, request-setup, enable / disable / archive still work.
Payments still needs the currency step in Modules if you have not set it.
This CLI will not accept an OAuth token, client secret, or Stripe account id (those fields 400), complete Stripe KYC in the terminal, or invent a second Stripe connect. Shop uses this same Express account.
frontpage modules payments status
frontpage modules payments connect
frontpage modules payments connect --no-open
frontpage modules payments disconnect --yes
frontpage modules payments request-setup --reason "Deposit button needs Stripe" shop
The store checkout runs on the same Stripe Express account as
payments.
connect needs an admin API key. It opens Frontpage (logged in), then Stripe.
Already-connected sites stay live. Shop still needs the shipping step in Modules if you have not set it.
request-setup remains valid for the editor amber bar.
frontpage modules shop
frontpage modules shop status [--site <slug>] [--json]
frontpage modules shop connect [--no-open]
frontpage modules shop disconnect [--yes] | CLI | API | What it does |
|---|---|---|
status | GET …/modules/shop | Connected?, charges enabled, shared-account note, owner connectUrl. Masks only. |
connect [--no-open] | POST action=connect | Returns the Frontpage start URL (not a raw Stripe link). --no-open for CI. No-ops if already connected. |
disconnect [--yes] | POST action=disconnect | Removes the shop module row. Does not delete the Stripe account (it lives on payments). |
Product catalog files stay in src/content/products/. Markup is
data-fp-add-to-cart / data-fp-cart-open. Not this command family.
For Square pickup menus use ordering.
This CLI will not accept tokens or account ids, set shipping / flat-rate from typed flags (that remains Modules / the editor), or invent a second Stripe account.
frontpage modules shop status
frontpage modules shop connect --no-open
frontpage modules shop disconnect --yes
frontpage modules shop request-setup --reason "You asked for a store. Stripe setup makes checkout live" ordering
Square cannot finish in the terminal. connect needs an editor API key.
It opens Frontpage (you must be logged in there), then Square. Already-connected sites stay live.
sync is the same catalog refresh as the Modules hub.
request-setup still lights the editor amber bar.
frontpage modules ordering
frontpage modules ordering status [--site <slug>] [--json]
frontpage modules ordering connect [--no-open]
frontpage modules ordering sync
frontpage modules ordering disconnect [--yes] | CLI | API | What it does |
|---|---|---|
status | GET …/modules/ordering | Merchant name, locations, card processing, last menu/locations sync, owner connectUrl. No tokens. |
connect [--no-open] | POST action=connect | Returns the Frontpage start URL (not a raw Square authorize link). Opens the browser unless --no-open. No-ops if already connected. |
sync | POST action=sync | Owner-triggered Square catalog + locations refresh. |
disconnect [--yes] | POST action=disconnect | Best-effort revoke + purge credentials. Past Square orders are kept. |
Markup is data-fp-order-menu + data-fp-order-cart-open in files or the editor agent.
Pickup/delivery fulfillment get/set is not in this CLI yet. Use Modules or the editor.
This CLI will not accept a Square token, code, or merchant id; set delivery ZIPs / prep time; or work when the platform has Square ordering switched off (the key 404s like other hidden modules).
frontpage modules ordering status
frontpage modules ordering connect
frontpage modules ordering connect --no-open
frontpage modules ordering sync
frontpage modules ordering disconnect --yes
frontpage modules ordering request-setup --reason "You asked for online ordering. Square setup makes it live" google_reviews
Owner-supplied Place ID or Maps URL. The server resolves the listing and seeds
the cache. No Autocomplete, no Places key, no Outscraper key in the CLI or v1 JSON.
Hub, editor agent (manage_google_reviews), and CLI share the same
curation JSON and google_review_cache.
frontpage modules google_reviews
frontpage modules google_reviews <action> [flags] [--site <slug>] [--json]
Bare frontpage modules google_reviews prints this key's CLI usage (exit 2). Not the
markup how-to. frontpage modules docs google_reviews stays the agent markup guide.
JSON: add --json.
| CLI | API | What it does |
|---|---|---|
connect --place-id <id> or --maps-url <url> | POST action=connect | Resolve → Places Details → seed (up to 100) → persist connection. Long HTTP timeout (~310s). |
resolve --place-id or --maps-url | POST action=resolve | Places Details only. No seed, no write. |
status | GET …/modules/google_reviews | Listing name, address, rating, cached count, write-a-review URL, curation summary. No review bodies. |
list-reviews [--limit 20] [--offset 0] [--stars 4,5] [--hidden] | GET …/modules/google_reviews/reviews | Paginated cache. Default page 20, max 100. Includes weight and visibility. |
hide --id / show --id | POST hide_review / show_review | Same curation store as the hub. Unknown id → 4xx, no write. |
set-stars 4,5 | POST set_star_filter | Replace the on-site star clicker set. At least one star required. |
set-sort newest|highest|lowest | POST set_review_sort | On-site sort after weights. |
set-weight --id --weight 1-1000 | POST set_review_weight | 1 clears the key. Higher numbers float first. |
reset-weights | POST reset_review_weights | Everyone back to weight 1. |
write-review-url | field on status | Print the platform write-a-review URL. Do not guess a Maps href. |
refresh | POST action=refresh | Same incremental + catch-up as hub “Refresh now.” Long HTTP timeout. |
disconnect [--yes] | POST action=disconnect | Deletes the connection row, not the review cache. --yes when stdin is not a TTY. |
Generic docs, request-setup, enable / disable /
archive, config --config '{…}'.
Do not write placeId or mapsUrl through
config. That skips resolve + seed. Use connect.
request-setup still puts an amber bar in the editor when the owner
does not already have a Place ID or Maps URL.
This CLI will not:
- Places Autocomplete or any Places / Outscraper key in the client.
- Invent stars, quotes, or a Maps href.
- Delete cached review rows (append-only).
- Replace the editor agent for layout and copy. Markup still goes through files or
frontpage prompt.
frontpage modules google_reviews
frontpage modules google_reviews connect --place-id ChIJ…
frontpage modules google_reviews connect --maps-url "https://maps.google.com/…"
frontpage modules google_reviews resolve --place-id ChIJ…
frontpage modules google_reviews status
frontpage modules google_reviews list-reviews --stars 4,5
frontpage modules google_reviews hide --id author:123
frontpage modules google_reviews show --id author:123
frontpage modules google_reviews set-stars 4,5
frontpage modules google_reviews set-sort highest
frontpage modules google_reviews set-weight --id author:123 --weight 100
frontpage modules google_reviews reset-weights
frontpage modules google_reviews write-review-url
frontpage modules google_reviews refresh
frontpage modules google_reviews disconnect --yes
frontpage modules google_reviews docs
frontpage inbox google_reviews
Human connect text: Connected Name. N reviews cached. Write-a-review: …
Ask-widget scores stay on frontpage inbox google_reviews.
Markup reminder
<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>
Full markup how-to: frontpage modules docs google_reviews.
Owner guide: Google Reviews for your website.