# 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.

Source: https://docs.use-mark.com/docs/capture-leads

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.use-mark.com`) 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:

```json
{ "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:

```json
{ "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

```bash
curl -X POST https://api.use-mark.com/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
  }'
```
