Capture leads and subscribers
The request and response contract for POST /leads and POST /subscribe, the two endpoints a tenant's site form posts to.
Mark records a lead or a newsletter signup through two public endpoints:
POST /leads and POST /subscribe. Your site keeps its own form, design,
and copy — these endpoints are the contract your form's submit handler
posts to.
Both endpoints live on the API host (for example
https://api.mark.lifewithdata.org) and take a JSON body over HTTPS. Life
With Data gives you the key value below; it identifies your workspace and
is safe to include in client-side code.
Before you integrate
- Your public form key. Every request body includes
key, the value Life With Data gave you for your workspace. - Your site's domain on the allowlist. Requests from a browser must come from an origin Life With Data has added to your workspace. An origin that isn't allowed gets refused.
- A consent checkbox. Both endpoints require an explicit consent acknowledgment before they record anything.
POST /leads
Call this when a visitor submits your lead form.
POST /leads
Content-Type: application/json| Field | Type | Required | Notes |
|---|---|---|---|
key | string | Yes | Your public form key. |
email | string | Yes | The visitor's email. |
consent | true | Yes | Must be exactly true. |
consentVersion | string | Yes | The version of the consent text you showed. |
pageUrl | string | Yes | The page the form was on, including any utm_* query parameters. |
renderedAt | number | Yes | Unix time in milliseconds when the form first rendered. Used to reject a submit that happens too fast to be human. |
name | string | No | |
phone | string | No | |
message | string | No | The visitor's message. |
personaId | string | No | An id from your Mark workspace, if your form asks which persona the visitor matches. |
answers | object | No | Any other named field on your form: a flat object of strings, numbers, or booleans, up to 40 keys. |
referrer | string | No | document.referrer at submit time. |
distinctId | string | No | Your analytics visitor id, if you have one. |
fbclid, gclid | string | No | Ad click ids from the page URL. |
honeypot | string | No | Leave any hidden honeypot input empty; a filled value marks the submission as a bot. |
A successful submission returns:
{ "leadId": "..." }Show your own thank-you message — Mark doesn't redirect anywhere. If the same email submits again while their lead is still new, Mark attaches the new message to that same lead instead of creating a second one.
POST /subscribe
Call this for a newsletter or marketing-only signup, where you don't want a
sales lead created. It takes the same fields as POST /leads except
phone, message, personaId, and answers, and returns:
{ "contactId": "..." }Responses
| Status | Meaning |
|---|---|
201 | Recorded. The body has leadId or contactId. |
200 with an empty body | The submission looked automated (a filled honeypot or an impossibly fast submit) and nothing was recorded. |
400 | The body is missing a required field, consent isn't true, or answers doesn't fit the shape above. |
403 | The request's origin isn't on your workspace's allowlist. |
404 | The key doesn't match a workspace. |
429 | Too many requests from the same address; try again shortly. |
Example
curl -X POST https://api.mark.lifewithdata.org/leads \
-H "Content-Type: application/json" \
-d '{
"key": "your-public-form-key",
"email": "visitor@example.com",
"message": "Interested in a quote",
"consent": true,
"consentVersion": "1.0",
"pageUrl": "https://your-site.com/contact?utm_source=instagram",
"renderedAt": 1735000000000
}'