Lead forms
Define a flat ArkType schema and register its component:
import { type, defineLeadForm } from '@shoplifter/presentation-sdk/leads';
export const contact = defineLeadForm({ id: 'contact', version: 1, label: 'Contact', persist: true, schema: type({ name: 'string', email: 'string.email' }), fields: [ { id: 'name', label: 'Name', kind: 'text' }, { id: 'email', label: 'Email', kind: 'email' }, ],});export const leadForms = [{ form: contact, component: 'src/LeadForm.astro' }];export const defaultFormId = contact.id;Create src/LeadForm.astro and add "leads": { "source": "src/leads.ts" } to shoplifter.json. Run shoplifter types to compile portable form descriptors. Each registered form owns an Astro component; the default also appears at /contact-us.
IDs and versions are immutable once registered. Increase the form version when its schema changes. persist: true retains submissions. The default is forwarding-only and requires a configured webhook.
Browser submission
Section titled “Browser submission”Import fetchLeadContext and submitLead from the leads entrypoint. Validate values with your form, fetch fresh context using its ID, and submit with an idempotency key. Retain that key for retries of unchanged values; generate a new one for a new submission.
const values = contact.validate({ name: 'Example', email: 'example@example.com' });const key = crypto.randomUUID();const context = await fetchLeadContext(contact.id);await submitLead(contact, context, values, key);This fragment belongs in the component’s browser handler, with contact imported from your form module. Disable submission controls whenever preview is true or mode !== 'published'; do not send test leads from fixture or preview pages. Context is bound to the resolved site, form and hostname. Never supply a tenant ID from browser input, embed secrets, or include real submissions in source.
The SDK exports LeadSubmittedEvent for webhook consumers. Publication activates the validated form definitions.

