Platform
Payments
Stripe setup, tax, Pay now, customer invoices, recurring billing, CLI and REST API.
Tested Payments walkthroughs
Start with these copyable workflows, then use the reference for the full command and field list. Response excerpts come from tested calls; extra fields are omitted and IDs may be normalized.
Install npm i -g frontpage-host@latest, set FRONTPAGE_API_KEY on your server, and replace acme with your site slug. Site roles, CLI access, and product permissions still apply. Use IDs and versions returned by your own calls.
CLI --json wraps success in ok, command and data. HTTP returns the resource body directly.
On this page:
- Create an Offering and use its returned markup
- Preview an invoice, review it, then send it once
- Handle invalid inputs before retrying
- Recurring billing requires customer consent
- Payments reference
Create an Offering and use its returned markup
Prerequisites: site API access and the role required for the operation. Admins create Offerings and send invoices; read and invoice entitlements still apply. Connect Stripe through the signed-in browser and recheck frontpage payments status before charging.
Save consultation.json. The address is required at checkout but excluded from the receipt:
{
"title": "Home consultation",
"priceCents": 10000,
"customizations": [
{
"customizationId": "job-address",
"label": "Property address",
"kind": "address",
"required": true,
"includeInReceipt": false
}
]
}
frontpage payments buttons create --id consultation --file consultation.json --site acme --json
{
"ok": true,
"command": "payments",
"data": {
"slug": "acme",
"button": {
"itemId": "consultation",
"title": "Home consultation",
"checkout": "instant",
"variants": [
{
"variantId": "default",
"priceCents": 10000,
"stock": null
}
]
},
"markup": "<button data-fp-pay=\"consultation\" data-fp-requires=\"pay\">Pay now <span data-fp-pay-bind=\"price\"></span></button>"
}
}
The full response includes receiptVariables. Use the returned markup in your page; catalog/Offering writes save immediately, while page edits go through normal CLI push and publish. A created Offering is not a payment. A field marked private is absent from its receipt variable list.
Preview an invoice, review it, then send it once
Use integer minor units. Save invoice.json:
{
"recipient": {
"name": "Docs test customer",
"email": "customer@example.com",
"address": {
"line1": "354 Oyster Point Blvd",
"city": "South San Francisco",
"region": "CA",
"postal": "94080",
"country": "US"
}
},
"description": "Website maintenance",
"amountCents": 12500,
"kind": "once",
"collection": "manual",
"dueDays": 14
}
frontpage payments invoices preview --file invoice.json --site acme --json
{
"ok": true,
"command": "payments",
"data": {
"slug": "acme",
"currency": "usd",
"subtotalCents": 12500,
"taxCents": 1234,
"totalCents": 13734,
"taxEnabled": true,
"billingContactChanged": false,
"previewHash": "ce04ecbbe777c9cf57cf7a0abcf4dbf13ceedfad10cee87db1fd5c8b765cf723"
}
}
These tax amounts came from the Stripe test account's configured address/tax settings; they are not a promised rate. Review the resolved recipient, terms, currency, totals and billingContactChanged. The full preview includes them. Preview does not issue an invoice or charge a customer.
Copy data.previewHash from the current preview into $PREVIEW_HASH. Choose a new stable $OPERATION_KEY for this intended invoice (16–120 permitted characters, for example invoice_maintenance_202610_001). Keep both the reviewed file and key for retries:
frontpage payments invoices send --file invoice.json --preview-hash "$PREVIEW_HASH" --operation-key "$OPERATION_KEY" --yes --site acme --json
{
"ok": true,
"command": "payments",
"data": {
"slug": "acme",
"invoice": {
"id": "aeb31529-2a3a-4cd5-9391-c52b27664c27",
"source": "invoice",
"status": "open",
"operationState": "complete",
"currency": "usd",
"subtotalCents": 12500,
"taxCents": 1234,
"totalCents": 13734,
"amountPaidCents": 0,
"amountRemainingCents": 13734,
"delivery": "accepted",
"paidAt": null
}
}
}
Result: this test invoice was issued and remained open, with zero paid. delivery:"accepted" is provider acceptance, not proof of email arrival. The full response includes a hosted invoice URL; treat it as a customer billing link, not a public asset.
Retry: resend the exact reviewed file with the same operation key and preview hash. Our real Stripe test replay returned the same invoice ID, not a second invoice. Do not generate a new key to retry a timeout. If the recipient or terms change, preview and review again; a stale/conflicting operation is not approval to charge.
Use data.invoice.id as $INVOICE_ID:
frontpage payments invoices get "$INVOICE_ID" --site acme --json
Check payment and delivery state there. Customer checkout completion and provider reconciliation determine payment; neither CLI success nor a return URL proves settlement. For an ordinary HTTP read:
curl --fail-with-body "https://app.frontpage.host/api/v1/sites/acme/payments/invoices/$INVOICE_ID" -H "Authorization: Bearer $FRONTPAGE_API_KEY"
The HTTP response contains slug and invoice directly, without the CLI wrapper.
Handle invalid inputs before retrying
A preview with "amountCents":12.5 returned:
{
"ok": false,
"command": "payments",
"error": {
"code": "bad_request",
"message": "amountCents must be an integer number of cents. $49.99 is 4999.",
"httpStatus": 400,
"exitCode": 2,
"requestId": "2fff6eca-ac37-4303-a30c-f30e6321829b"
}
}
Correct the units, then preview again. Retrying the same invalid payload cannot succeed. For a timeout or recoverable provider failure, inspect the operation's current state and reuse its original key; do not create a second financial operation to work around uncertainty.
Recurring billing requires customer consent
| Intent | Expected path | What success means |
|---|---|---|
| One invoice | Preview → review → invoices send → inspect. |
An invoice exists; check whether it is still open or paid. |
| Automatic recurring collection | Preview recurring terms → send reviewed arrangement → customer accepts the scoped invitation. | awaiting_authorization is not active consent and does not authorize a charge. |
| Stop future billing | Preview the cancellation where required, then confirm the recurring cancellation command. | Future billing stops; previously paid invoices/history remain. |
| Pause Payments site-wide | Read availability preview, review affected records, then confirmed availability off. |
New charging blocks immediately; inspect remaining/failed work until the pause completes. |
The recurring, consent and cancellation reference below contains their full payloads, lifecycle states, commands and failure behavior. The documentation fixture tested a one-time invoice and its replay, not a live subscription or a live charge.
Payments reference
Requires frontpage-host 0.2.1 or later. Install with npm i -g frontpage-host@latest.
Frontpage keeps one Stripe Express account per site. Store owns products, cart checkout, fulfillment, buyer service fees, the default receipt and orders. Payments owns Stripe setup, the shared currency, tax, payment activity, saved Offerings behind Pay now buttons, their receipt templates and customer invoices. Frontpage subscription billing stays separate.
Use frontpage help payments --json for discoverable command help. Normal
--site, authentication, --json, --quiet and exit-code rules apply. The API
requires Scale, Enterprise or Internal CLI access. This does not remove product
permissions or invoice entitlements.
A payment button without a Store
Use a saved Offering for a consultation, donation, deposit or digital download. Creating, updating or restoring an Offering requests Payments setup when needed. It does not enable Store or ask for shipping or pickup. Payments that you turned off stays off.
The save response includes the saved button, checkout: "standalone", requiresFulfillment: false, and setup.status plus the existing signed-in admin Stripe handoff. Accepting payments requires an active Offering and ready Payments. A successful save alone does not make checkout live. Reading an Offering reports readiness under payment.
Prepare a digital bundle
Use an existing uploaded resource or an approved download URL. One combined PDF can contain both an e-book and workbook. Keep the Offering inactive until its resources and purchase receipt are ready.
frontpage payments buttons create --id digital-bundle --title "E-book + workbook" --price-cents 2500 --active false --json Relevant save fields while Stripe still needs setup:
{
"checkout": "standalone",
"requiresFulfillment": false,
"button": {
"itemId": "digital-bundle",
"active": false
},
"setup": {
"status": {
"state": "not_connected",
"ready": false
},
"url": "https://app.frontpage.host/api/payments/connect/start?slug=YOUR_SITE"
}
} Use Emails to save a template with a real download link, attach its returned ID, and preview it. A receipt that only says thank you does not deliver the file.
frontpage payments buttons update digital-bundle --receipt-template-id TEMPLATE_UUID --json
frontpage payments buttons receipt-preview digital-bundle --json
frontpage payments buttons update digital-bundle --active true --json receipt-preview requires CLI 0.2.10 or later. The other commands work in earlier releases. The preview uses the same renderer as purchase receipts. It sends no email, makes no charge and reads no customer record. With a custom template, the response includes preview: true, receipt: "custom", the Offering and template IDs, subject, html and text. Without a template, it returns receipt: "stock" and explains the standard purchase receipt.
Preview through the API
POST /api/v1/sites/YOUR_SITE/payments/buttons/digital-bundle/receipt-preview
Content-Type: application/json
{"vars":{"buyer_name":"Casey"}} Requires editor access to the site. Use only the exact names returned in receiptVariables. Sample values must be strings of at most 32,000 characters. Private excluded answers and another site's Offering or template are rejected. The response follows the normal API envelope.
Verified payment triggers the existing purchase receipt job, retry identity and email limits. Do not send the receipt manually or subscribe the buyer to marketing. Public file links can be forwarded; this workflow does not create buyer-only or expiring downloads or email attachments. Marketing's postal-address requirement stays in place for marketing sends; it does not block transactional purchase receipts.
If a create request times out or returns an existing-ID conflict, read that ID and update the same Offering. Do not create another payment object to force a retry.
Access and output
| Operation | Minimum role | Additional rule |
|---|---|---|
| Connection status | Viewer | Site membership |
| Private activity, invoices, arrangements, tax and Pay now reads | Editor | Site membership |
| Existing tax settings and registration writes | Editor | Explicit supplied jurisdiction/address and confirmation |
| Stripe browser setup, payment availability, Pay now writes, invoice sends and billing links | Admin | Confirmation where specified |
| New invoice, new arrangement or resume | Admin | Growth or higher and Payments ready |
| Historical reads, refresh, void and future cancellation after downgrade | Existing role above | No new invoice entitlement required |
All routes authorize the API-key owner against the requested site. A different
site's record returns 404. Request fields cannot grant a role, change the trusted
site or choose a connected account. JSON success uses the existing CLI envelope
{ "ok": true, "command": "payments", "data": { ... } }. Failure uses the
existing error envelope with request ID and exit code. Human mode prints the
returned fields, including partial outcomes. A successful HTTP request does not
mean a payment settled or an email arrived.
Setup, availability and history
frontpage payments status
frontpage payments settings --json
frontpage payments connect --no-open
frontpage payments availability preview --json
frontpage payments availability off --yes
frontpage payments availability on --yes
frontpage payments activity list --json
frontpage payments activity get <record-uuid> --source order Connect returns the existing signed-in admin browser URL. Without --no-open,
human mode opens it. The CLI does not accept Stripe terms, finish onboarding or
create another account to get around a restriction. Recheck status after the
browser flow. Account existence or a return URL alone does not prove readiness.
Payments saves the shared currency during the existing Stripe setup. Store, Offerings, invoices and Printify read that same currency. It is read-only; do not set a second Store currency. Existing payments retain their captured currencies.
Payments off first blocks new charges, then stops recurring billing and voids
unpaid issued invoices. It preserves paid history and the Express account. Review
the counts with availability preview. off reads current counts before its
confirmed write; a changed set returns 409. A bounded request may return
pause.complete:false, remaining counts and failed record IDs. New charging stays
blocked. Resolve errors and repeat off; do not turn Payments on while a pause is
incomplete. A payment already processing may settle and keeps its real result.
Turning Payments on rechecks Stripe readiness. It does not enable Store, restore void invoices, resume arrangements or collect missed periods. Store off affects only Store; it leaves standalone payments and billing running.
Activity combines the latest 200 Store orders and 200 Pay now payments with up to
100 invoices and 100 recurring arrangements each. mayHaveMore states when older
records may exist. It preserves captured currencies, amounts and payment states.
Recurring amounts are per-period amounts before tax. Use invoice/recurring list
commands for their paginated history. Get an order/payment with its explicit
source; equal IDs in different tables do not identify the same record.
Activity uses Customer, Type, Status, Amount and Date. Date is the verified payment time, not creation or due date. Unpaid invoices and recurring arrangements show no payment date; historic payments without a verified timestamp remain unknown. Summary counts cover the full site, independent of recent-list caps.
Tax
frontpage payments tax get --json
frontpage payments tax settings --file tax-settings.json --yes
frontpage payments tax registrations list
frontpage payments tax registrations add --country US --state CA --yes
frontpage payments tax registrations end <tax-registration-id> --yes Tax settings JSON contains country, line1, city, optional state, postal
and an existing supported taxCode. GET returns current choices and registrations.
Use the business's actual address and registrations. Frontpage does not choose
jurisdictions or provide tax advice. End uses the exact registration ID returned
by Stripe. These operations reuse current connected-account tax helpers. They do
not expose Account Session secrets or a second tax calculator. An invoice preview
blocks if tax settings cannot be confirmed; it does not silently drop tax.
Invoice input and review
For an existing CRM person:
{
"recipient": { "personId": "<crm-person-uuid>" },
"description": "Website maintenance",
"amountCents": 12500,
"kind": "once",
"collection": "manual",
"dueDays": 14
} To add or match a CRM customer when sending, replace recipient with:
{
"name": "Customer name",
"email": "customer@example.com",
"address": {
"line1": "Customer billing street",
"city": "City",
"region": "CA",
"postal": "94080",
"country": "US"
}
} address also accepts line2. Supply a real billing address when automatic tax
needs it. A CRM person must be visible and have a valid billing email. Inline
matching stays within the site and does not grant marketing permission. Preview
creates no CRM person, Stripe Customer, invoice, invitation or charge.
Money fields use integer minor units in the account currency despite the existing
Cents field names. USD 12500 means $125.00; JPY 12500 means ¥12500. The current
amount range is 50 through 10,000,000 minor units. Description contains 1–500
characters. dueDays is 7, 14 or 30; omitted means 7. A one-time invoice uses
manual collection and cannot carry recurring date/cadence fields.
frontpage payments invoices preview --file invoice.json --json
frontpage payments invoices send --file invoice.json \
--preview-hash <hash-from-preview> --operation-key <stable-uuid> --yes --json Preview returns the normalized recipient/terms, currency, subtotal, tax, total,
tax status and previewHash. When billingContactChanged is true, review the CRM email, name and address and pass --confirm-billing-contact to update the saved Stripe contact for future billing. This does not rewrite issued invoices or prior consent. Send repeats those exact terms and hash. It rechecks
readiness and totals before finalization/delivery. Changed terms or totals return
409. Common flags can replace simple JSON fields: --person or --email with
--name, --description, --amount-cents, --kind, --collection, --due-days,
--cadence, --first-date, --time-zone. Nested addresses use --file.
The operation key contains 16–120 letters, digits, dots, colons, underscores or hyphens and starts with a letter/digit. A UUID is suitable. Preserve the same key and input after a timeout or uncertain result. Do not use a new key to force a send. Repeated requests return the same local/Stripe invoice rather than charging again. If Stripe's safe retry window has passed, reconcile the saved object first.
Invoice management
frontpage payments invoices list --limit 50 --offset 0 --json
frontpage payments invoices list --arrangement <arrangement-uuid> --json
frontpage payments invoices get <invoice-uuid>
frontpage payments invoices refresh <invoice-uuid>
frontpage payments invoices resend <invoice-uuid> --operation-key <stable-uuid> --yes
frontpage payments invoices void <invoice-uuid> --yes List accepts limit 1–100 and offset 0–100000; it returns nextOffset or null.
Records include immutable recipient/terms, actual currency/subtotal/tax/total,
paid/remaining amounts, due/issued/paid dates, Hosted Invoice Page/PDF, delivery
and any last error. Expected platform fee and actual collected fee are separate.
An unpaid invoice does not claim a collected fee. Source account/setup secrets
and customer-access nonces are not returned.
Resend only accepts an open invoice and uses its existing saved billing customer. It never creates a replacement invoice. Void requires confirmation and cannot restore an invoice after Stripe confirms it is void. A payment race preserves an actual paid result. An unsent draft's cancellation is recorded separately from Stripe's real draft state. There is no manual mark-paid, refund or raw charge API.
Stripe sends invoice emails and hosts payment/recovery. delivery:accepted means
the send API accepted the request, not that the email arrived or was opened.
Stripe test mode may suppress email delivery; provider acceptance is the most
that a test-mode report may claim.
Recurring manual invoices and autopay
Use the same recipient, description, amount and due window. Set kind:recurring,
cadence, firstDate and timeZone. Collection is manual or autopay.
{
"recipient": { "personId": "<crm-person-uuid>" },
"description": "Monthly maintenance",
"amountCents": 12500,
"kind": "recurring",
"collection": "manual",
"cadence": "monthly",
"firstDate": "2026-12-01",
"timeZone": "America/Chicago",
"dueDays": 14
} Supported cadences are weekly, fortnightly, monthly, quarterly, yearly.
The first date is YYYY-MM-DD in an explicit IANA zone. Billing begins at 09:00 in
that zone. Choose a future date. Stripe owns later native calendar periods,
including short months/leap years. No Frontpage recurring scheduler, backfill or
proration is added.
New recurring arrangements use Stripe's native platform fee: 2% of the invoice total, including tax, unless the existing server fee setting is zero. The saved arrangement freezes that server-controlled rate before billing starts. For example, a $100 invoice plus $9.88 tax has a $2.20 platform fee. The fee is deducted from the business's proceeds, not added as a customer surcharge. Stripe's own processing and Billing/Invoicing charges are separate. Store, Pay now and one-time invoice fee calculations keep their existing basis. Reconciliation records expected and actual fees; it does not set the fee or collect recurring payments. Stripe handles collection even if a webhook is late.
frontpage payments recurring preview --file recurring.json --json
frontpage payments recurring create --file recurring.json \
--preview-hash <hash> --operation-key <stable-uuid> --yes
frontpage payments recurring list --limit 50 --offset 0 --json
frontpage payments recurring get <arrangement-uuid>
frontpage payments recurring refresh <arrangement-uuid>
frontpage payments recurring cancel <arrangement-uuid> --yes
frontpage payments recurring resend-invitation <arrangement-uuid> \
--operation-key <stable-uuid> --yes Manual collection creates a native future Stripe schedule. It does not issue an immediate first invoice. Stripe creates one invoice per period and emails the hosted payment link. Autopay create saves a pending authorization and sends an invitation. The customer reviews the amount, tax, cadence and first date, agrees, and finishes Stripe-hosted setup on the same connected account/customer. Only verified setup can start the schedule. A saved method, setup return query or owner flag alone never supplies consent. Setup itself is not a payment.
Arrangement cancellation stops future billing without changing issued invoices or adding a final prorated charge. Payments off pauses arrangements by stopping their native future schedule. Only paused arrangements may resume. Preview the same recipient/amount/cadence/collection with a new future date, then:
frontpage payments recurring resume <paused-arrangement-uuid> \
--first-date 2026-12-01 --preview-hash <hash-from-preview> \
--operation-key <stable-uuid> --yes Resume creates a linked future arrangement with no missed-period invoices. A second different resume request is refused. Autopay seeks fresh customer consent. Changed terms require cancellation and a new reviewed request; there is no hidden edit/proration action. Invitation resend applies only while awaiting authorization. It expires/replaces an unfinished old setup and keeps its own retry key.
Customer billing access
frontpage payments customers send-billing-link <customerMapId> \
--operation-key <stable-uuid> --yes
frontpage payments customers billing-link <customerMapId> \
--operation-key <stable-uuid> --json Use customerMapId from the site's invoice/arrangement. It is not a raw Stripe
customer ID. Send uses the mapped customer's current saved Stripe billing email;
the caller cannot choose another recipient. Links expire after 30 minutes. A raw
returned link is sensitive: do not log/share it. The website agent only sends
links; it cannot request raw customer access.
Customer access needs no owner login. It stays scoped to site/account/customer and uses a small customer page plus the connected-account Stripe portal. The customer can pay an invoice, change the saved method, cancel future billing or choose manual payment for future invoices. These actions do not pay an existing balance, authorize another arrangement or expose Frontpage subscription billing. Authorization invitations expire after 24 hours; an expired link requires a new invitation. A completed authorization exposes Manage billing.
Offerings behind Pay now
Every website Pay now payment requires a saved, registered Offering in Payments. Store carts and invoices keep their own saved payment records. Markup, an arbitrary amount, a nonshipping Store item or an old instant flag cannot authorize a charge. The workspace, CLI and website agent use the same Offering service and existing item/price storage.
frontpage payments buttons list --limit 50 --offset 0 --json
frontpage payments buttons create --id consultation --title "Initial consultation" --price-cents 10000
frontpage payments buttons get consultation
frontpage payments buttons update consultation --title Consultation --price-cents 12500 --active true
frontpage payments buttons update consultation --receipt-template-id TEMPLATE_UUID
frontpage payments buttons update consultation --default-receipt
frontpage payments buttons update consultation --active false
frontpage payments buttons archive consultation --yes
frontpage payments buttons list --archived true
frontpage payments buttons restore consultation --yes Create/update accepts --file offering.json. Price, details, questions and template choice save together. IDs use 2–40 lowercase kebab characters. Customer-chosen amounts use --amount open --min-cents 1000 --price-cents 1000 in the connected account's minor units. Currency comes from Stripe; a page cannot supply it. Shipping and fulfillment belong to Store.
Create/update returns the saved object and this markup. Bind it in the same change:
<button data-fp-pay="consultation" data-fp-requires="pay">Pay now <span data-fp-pay-bind="price"></span></button> The platform loads the saved price, minimum and availability. Do not use data-amount, substitute a Store product ID, build card inputs, or call Stripe directly. Paused, archived, wrong-site and unregistered Offerings cannot charge. Archive removes an Offering from new checkout and retains history; restore can bring it back. Standalone Offering management is available on every plan; charging still requires normal Payments readiness.
--receipt-template-id selects an available Emails template from the same site. --default-receipt uses the existing site default; the flags are mutually exclusive. Checkout freezes that choice. A deleted template uses the stock receipt, with the same retry identity to prevent a second send. Receipts follow verified payment and existing email limits; they do not grant marketing consent.
Payments drawer design
Pay now Offerings do not need a Store cart or Cart button. Fixed payments without questions proceed to Stripe Checkout. Custom amounts and saved questions use the platform's independent Payments drawer. Changing questions in Payments, the CLI or agent tools updates the next opening without republishing the site.
A default drawer works without a template. To design your own, include one shared template using the following hooks. The platform fills the price, amount field, saved questions and errors and handles submission. Keep forms, scripts, manually written questions and Store hooks out of this template.
<template data-fp-payment-drawer>
<section data-fp-payment-panel>
<header><h2 data-fp-payment-bind="title"></h2><button data-fp-payment-close>Close</button></header>
<p data-fp-payment-bind="description"></p>
<p data-fp-payment-bind="price"></p>
<div data-fp-payment-amount></div>
<div data-fp-payment-fields></div>
<p data-fp-payment-error></p>
<button data-fp-payment-confirm>Continue to payment</button>
</section>
</template> Defaults use the soft fp-defaults CSS layer. Override appearance with your own root class and global CSS, as with Store templates. Payments supports --fp-payment-bg, --fp-payment-fg, --fp-payment-border, --fp-payment-radius, --fp-payment-width, --fp-payment-padding, --fp-payment-action-bg, --fp-payment-action-fg and --fp-payment-focus. Style generated fields beneath your root using .fp-payment-customization and .fp-payment-address. Design changes use normal website publishing. Stripe Checkout keeps its supported branding controls.
Customer details
Save questions on the Offering through customizations, using the same shared fields as Store. Example offering.json:
{
"title": "Home consultation",
"priceCents": 10000,
"customizations": [
{
"customizationId": "job-address",
"label": "Property address",
"kind": "address",
"required": true
},
{
"customizationId": "contact",
"label": "Preferred contact",
"kind": "choice",
"values": [
"Phone",
"Email"
]
},
{
"customizationId": "instructions",
"label": "Access instructions",
"kind": "textarea",
"maxLength": 1000,
"includeInReceipt": false
}
]
} frontpage payments buttons update consultation --file offering.json --json
frontpage payments buttons get consultation --json
frontpage payments activity get ORDER_UUID --source order --json At most eight questions. Types: short text (up to 200 characters), long text (up to 2000), choice (up to 20 values), checkbox, email, phone, number, date and structured address. API kinds are text, textarea, choice, checkbox, email, phone, number, date and address. Text accepts maxLength; numbers accept min/max. Choices require explicit selection and required checkboxes require an affirmative check.
The existing payment drawer renders and validates these questions before Stripe Checkout. Zero and false retain their meaning. A service address does not enable shipping; a preferred date is a request, not a confirmed booking. Intake answers cannot change money, currency or tax, and do not overwrite People. An unrelated page form does not automatically become a paid submission.
Keep stable customizationId values when renaming/reordering. Replacing the array changes future checkouts. Paid records retain the captured labels, kinds and answers under Customer details, even after questions change or disappear. Answers default out of receipts; opt in per field with includeInReceipt:true. Keep answers out of page markup, URLs and Stripe product names. Files, conditional questions, paid selections and public recurring Offering signup are not supported.
A normal request such as “add a $100 consultation button and ask for the property address” lets the website agent find/reuse or create an Offering and bind its button in the same turn. Customers do not need to know object IDs. Missing essential terms such as price need clarification. The agent cannot authorize customer autopay consent.
Confirmation emails from customer answers
Use the existing Emails templates. There is no separate email engine or variable mapping UI. Offering create, get and update responses include receiptVariables: the exact names and labels available for that Offering. Read the response before writing the template.
The five purchase variables are buyer_name, buyer_email, total, currency and items. A field with includeInReceipt:true also exposes answer_<customizationId>. For a saved field called review-date, use {{answer_review-date}}. Names follow stable IDs, so renaming a label does not break its email. Private answers never become variables or enter the items summary.
frontpage payments buttons get portfolio-review --json
frontpage mail template create --file confirmation.json --json
frontpage payments buttons update portfolio-review --receipt-template-id TEMPLATE_UUID --json
frontpage payments buttons get portfolio-review --json
frontpage mail template get TEMPLATE_UUID --json Declare each used name in the template's variables array, then use it in its subject, preview or blocks. The server rejects attachment or question changes when the template uses an unavailable answer. Detach the template, change it, or keep the field included before retrying. Shared templates must work for every unarchived Offering attached to them. To restore an Offering with an incompatible template, use --default-receipt or fix its template first.
Confirmed purchases use the captured answers, not later field edits. Blank optional answers stay blank; private answers stay private. The normal verified-payment receipt path sends once and retains its existing limits and stock fallback if a template is deleted. Do not manually send the customer's receipt or enrol them in marketing. Forms keep their existing input-name variables and confirmTemplateId; Offerings use receiptTemplateId. The website agent uses these same services.
Activity and dates
Activity includes all saved Offerings, including archived ones in the management list. Payment summaries show gross receipts, successful-payment count and average payment over the last 30 days across Store, Offerings and invoices. They cover the full ledger, not only the visible recent rows. Each currency has its own total; currencies are never added together. Gross receipts precede fees and refunds.
The Date column shows when a verified payment happened. An unpaid invoice or recurring arrangement has no payment date. New payments save the webhook event's date. The activity service can recover recent missing dates from matching Stripe events without replaying purchases, emails or fulfillment. Stripe retains Events for 30 days; older or unverified dates remain unavailable rather than using checkout creation as a payment date.
REST paths
All paths below use /api/v1/sites/{slug}/payments. GET query fields match the
documented filters. POST/PATCH fields match the reviewed domain inputs above.
Financial consequences need confirm:true; stable retryable sends additionally
need operationKey. This is a resource API, not arbitrary action dispatch.
| Method | Resource | Result |
|---|---|---|
| GET | /status, /settings | Safe current connection/readiness and read-only currency |
| POST | /connect | Signed-in admin browser handoff |
| GET / POST | /availability | Pause counts/hash or confirmed availability outcome |
| GET | /activity, /activity/{order|payment|invoice|recurring}/{id} | Current source-aware activity/detail |
| GET | /tax, /tax/registrations | Existing tax state/options/registrations |
| POST | /tax/settings, /tax/registrations | Existing tax settings/registration write |
| POST | /tax/registrations/{id}/end | End the specified registration |
| GET / POST | /buttons | List / create an explicit Pay now item |
| GET / PATCH | /buttons/{id} | Read / update that explicit Pay now item |
| POST | /buttons/{id}/{archive|restore} | Confirmed archive / restore |
| POST | /invoices/preview, /invoices/send | Read-only review / send |
| GET | /invoices, /invoices/{id} | Paginated invoices / detail |
| POST | /invoices/{id}/{refresh|resend|void} | Reconcile, resend or confirmed void |
| POST | /recurring/preview, /recurring | Read-only review / create request |
| GET | /recurring, /recurring/{id} | Paginated arrangements / detail |
| POST | /recurring/{id}/{refresh|cancel|resume|resend-invitation} | Scoped lifecycle action |
| POST | /customers/{customerMapId}/{billing-link|send-billing-link} | Private customer access |
Common failures: 400 invalid input/retired write; 401 no authentication; 403 role, CLI tier or product entitlement; 404 wrong site/record; 409 stale review, conflict, unfinished pause or unknown provider outcome; 429 existing rate limit; 503 missing rate-limit service, tax-read failure or provider failure. Keep the request ID when reporting a failure. Never retry a financial send with a new key just because an HTTP response was lost.
Breaking release
Version 0.2.0 removes Store connect/status/disconnect/payments, writable currency
and checkout mode from CLI/public API/agent contracts. Use payments for money
operations. Store remains cart-only and keeps existing resource paths under
/shop. Saved products/orders, Express accounts, current visitor hooks,
/__pay, Connect webhooks and old browser callbacks remain intact. Old binaries
may fail on retired endpoints; no compatibility alias or data conversion runs.