Create a signup form
Authorizations
A secret API key. Publishable keys cannot reach this API. A key may carry an expiry, and an expired key is refused exactly like an unknown one, with a 401 that names no reason; check the key's expires_at in the dashboard rather than inferring it from a response. When a merchant rolls a key's secret they choose a grace window of up to 3 days, and for its duration BOTH the new secret and the one it replaced authenticate, so an integration moves over on its own deploy schedule instead of at the instant the button is pressed. Move before the window closes: after it, the old secret is refused. Nothing else about this contract moves with a roll. The key keeps its id and its scopes, so the only thing an integration updates is the credential itself.
Headers
A unique key per logical write. Replaying a request with the same key returns the first response byte for byte instead of applying the write twice.
Body
A FULL DEFINITION on both writes, which is why the create and the replace take almost the same body and why the update is PUT rather than PATCH. An omitted optional key is CLEARED rather than carried over, so read the form and send back what you want it to end up with. THE REQUIRED FIELDS INCLUDE consent AND status, which look defaultable and are not: an omitted consent decodes to empty modes and the form silently stops asking anyone to agree to anything while it keeps collecting addresses, and an omitted status decodes to draft and takes a live form off the storefront. Neither raises an error and neither is visible until the merchant notices their signups stopped. type IS REQUIRED HERE AND CANNOT BE CHANGED LATER. The replace refuses the key outright rather than ignoring it. Names are unique per store and case-insensitive; a duplicate is 409 name_taken rather than a second form.
How the form is presented: inline in the page, a centred modal, a panel sliding in from an edge, or a full page of its own. IMMUTABLE after creation, because the widget renders each differently enough that switching one into another under live traffic is a surprise rather than a feature. Create a second form instead.
embed, popup, flyout, landing Unique per store and compared case-insensitively, which makes it a stable handle a migration tool can re-run against. A duplicate is 409 name_taken. Merchant-facing only; a shopper never sees it.
120ONLY active is served to shoppers. Setting it here activates or pauses the form as part of the same write; the dedicated activate and pause routes are the way to do it without touching the definition.
draft, active, paused The ordered input list. EXACTLY ONE field must have type email: it is the identity a submission keys a contact on, and a form with none has nothing to key while a form with two is an ambiguity on the identity path. An email field is always stored as required whatever you send, because an optional identity is not an identity.
1 - 12 elementsWhere, when and to whom the form shows.
THE COMPLIANCE-CRITICAL BLOCK. Whatever text sits here is what every submission taken afterwards quotes verbatim as the merchant's proof that a specific person agreed to specific words.
What the shopper sees after submitting. OPTIONAL, and omitting it on a replace CLEARS it: the storefront then shows its own default confirmation, which is a real configuration rather than an error.
Response
Created
