Developers Modules

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/formsdestinations 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, or signup for a destination.
  • Use request-setup forms as 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.