Lead Hub API
Internal API for ingesting leads across sites.
POST /api/leads
Accepts a lead, verifies contact info via XVerify, saves to Supabase, and syncs to Beehiiv.
Authentication
All requests must include:
x-api-key: <LEAD_HUB_API_KEY>Missing or incorrect key returns 401 Unauthorized.
Request Fields
Content-Type: application/json
Required
| Field | Type | Default | Description |
|---|---|---|---|
| source | string | Originating site/form (e.g. "weekly-investor") | |
| string | Valid email address |
Core
| Field | Type | Default | Description |
|---|---|---|---|
| phone | string | — | Phone number — used for XVerify |
| firstName | string | — | |
| lastName | string | — |
Consent
| Field | Type | Default | Description |
|---|---|---|---|
| marketing_consent | boolean | false | |
| sms_consent | boolean | false | |
| double_opt_override | "on" | "off" | "not_set" | "not_set" | Overrides Beehiiv double opt-in. "on" sends confirmation email before activating. "off" activates immediately. "not_set" uses publication default. |
Tracking
| Field | Type | Default | Description |
|---|---|---|---|
| universal_leadid | string | — | TrustedForm / Universal LeadId |
| aff | string | — | Affiliate ID |
| s1 – s20 | string | — | Sub-ID / click-tracking params (s1 = primary transaction ID) |
UTM
| Field | Type | Default | Description |
|---|---|---|---|
| utm_source | string | — | |
| utm_medium | string | — | |
| utm_campaign | string | — | |
| utm_term | string | — | |
| utm_content | string | — |
browserData (object, optional)
| Field | Type | Default | Description |
|---|---|---|---|
| deviceType | string | — | |
| operatingSystem | string | — | |
| browser | string | — | |
| browserVersion | string | — | |
| screenResolution | string | — | |
| viewportSize | string | — | |
| language | string | — | |
| timezone | string | — | |
| timezoneOffset | number | — | |
| connectionType | string | — | |
| pageUrl | string | — | |
| pageTitle | string | — | |
| landingPage | string | — | |
| referrer | string | — | |
| sessionId | string | — | |
| isReturningVisitor | boolean | — | |
| timeOnPage | number | — | seconds |
| formCompletionTime | number | — | seconds |
locationData (object, optional)
| Field | Type | Default | Description |
|---|---|---|---|
| country | string | — | |
| region | string | — | |
| city | string | — | |
| postal | string | — | |
| latitude | number | — | |
| longitude | number | — |
Example Request
curl -X POST https://your-domain.com/api/leads \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-d '{
"source": "weekly-investor",
"email": "jane@example.com",
"phone": "5551234567",
"firstName": "Jane",
"lastName": "Doe",
"marketing_consent": true,
"sms_consent": false,
"double_opt_override": "off",
"aff": "aff123",
"s1": "click_abc",
"utm_source": "facebook",
"utm_medium": "cpc",
"utm_campaign": "q1-promo"
}'Responses
200 OK
{
"success": true,
"leadId": "uuid-string",
"source": "weekly-investor",
"verified": { "email": true, "phone": true }
}400 — Validation failed
{ "error": "Validation failed", "details": { ... } }Also returned when XVerify marks the contact as invalid (production only).
401 — Unauthorized
{ "error": "Unauthorized" }409 — Duplicate
{ "error": "Already submitted. Check your email for confirmation.", "isDuplicate": true }Same email or phone + source submitted within 5 minutes.
503 — Verification unavailable
{ "error": "Verification service unavailable. Please try again." }500 — Internal error
{ "error": "Internal server error" }Behavior Notes
- Deduplication — same email or phone + source within 5 minutes is rejected with 409.
- Verification — email and phone checked via XVerify before saving. Invalid contacts rejected in production. Set
XVERIFY_SKIP_ON_ERROR=trueto bypass on errors. - Beehiiv sync — happens asynchronously after the response is returned.
beehiiv_syncedon the lead record is updated once confirmed. - IP / User-Agent — captured automatically from request headers.