Linkit

Marketing Leads

Public landing-form intake and the commercial pipeline list

Marketing Leads

The previous deployment had no /api/v1/leads: its landing page wrote the leads collection directly, through POST /api/collections/leads/records, and the commercial portal listed the same collection. Both 404 on this stack. These two routes are the replacement.

The leads table has no organization_id. It is platform-wide marketing intake, not a tenant collection.


Quick Reference

EndpointAuthPurpose
POST /api/v1/leadsNoLanding contact form
GET /api/v1/leadsCommercialPipeline list, newest first, cap 500

The Lead Object

{
  "id": "abc123xyz789def",
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "phone": "+966500000000",
  "position": "CTO",
  "plan": "Flat Rate (interest)",
  "erp": "",
  "contact_reason": "Website: Get in Touch",
  "notes": "Company: DFS",
  "created": "2026-09-10 10:00:00.000Z"
}

Fields

FieldTypeDescription
idstringColumn default (pb_new_id())
namestringRequired on create. Compared untrimmed — whitespace-only is accepted
emailstringRequired first. Must look like local@host.tld
phonestringOptional
positionstringOptional
planstringOptional interest plan label from the landing form
erpstringOptional; JSON null stores as empty
contact_reasonstringOptional landing-form reason
notesstringOptional; JSON null stores as empty
createdstringColumn default timestamp

Create a Lead

Public. No token. This is what the React landing createLead posts.

POST /api/v1/leads
curl -X POST "https://api.linkit.works/v1/leads" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "phone": "+966500000000",
    "position": "CTO",
    "plan": "Flat Rate (interest)",
    "erp": null,
    "notes": "Company: DFS",
    "contact_reason": "Website: Get in Touch"
  }'
const lead = await fetch("/api/v1/leads", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "Ada Lovelace",
    email: "ada@example.com",
    phone: "+966500000000",
    position: "CTO",
    plan: "Flat Rate (interest)",
    erp: null,
    notes: "Company: DFS",
    contact_reason: "Website: Get in Touch",
  }),
}).then((r) => r.json());

Response 200

The stored row (same shape as the object above).

Validation order

The same ladder as Go validateLeadRecord / hooks::leads:

OrderConditionMessage
1email emptymissing required field: email
2email not local@host.tldinvalid email format
3name emptymissing required field: name

Store failure is failed to create lead. Error bodies carry the platform envelope {data, message, status} with Sentenize on message.

Do not post /api/collections/leads/records against this host. The collection API is not served.


List Leads

Commercial-gated. Merchant JWTs receive 403. Newest first, hard cap 500 (COMMERCIAL_MAX_FETCH_SIZE).

GET /api/v1/leads

Response 200

{
  "leads": [
    {
      "id": "abc123xyz789def",
      "name": "Ada Lovelace",
      "email": "ada@example.com",
      "phone": "+966500000000",
      "position": "CTO",
      "plan": "Flat Rate (interest)",
      "erp": "",
      "contact_reason": "Website: Get in Touch",
      "notes": "Company: DFS",
      "created": "2026-09-10 10:00:00.000Z"
    }
  ]
}

The envelope is {leads:[…]} so the React commercial pipeline does not guess a collection path. List failure is failed to list leads.