Reactive CRM Site Leads Integration Guide ← Integration guide

Getting Leads from Your Website

Instructions for integrating your website form with ReactiveCRM: capturing form submissions as leads, sending marketing attribution events, and verifying the result.

1. Overview

The recommended flow consists of two requests:

  1. The site sends the form data to its own backend.
  2. The site backend creates a lead in CRM via POST /api/leads/create.
  3. After successful creation, the backend sends a lead_submitted marketing event via POST /api/dashboard/marketing/events, passing the received leadId.
  4. CRM stores UTM tags, advertising identifiers and the referrer for marketing attribution.
text
Website form
    |
    v
Site backend / server-side proxy
    |  POST /api/leads/create
    v
ReactiveCRM -> leadId
    |  POST /api/dashboard/marketing/events
    v
Marketing attribution and dashboard

Do not send CRM JWT and secrets directly from the browser. Use the site backend or a serverless function so the CRM token and service identifiers are never exposed to the visitor.

2. Authentication and Base URL

All requests run in the context of the relevant tenant and require CRM authorization:

http
Authorization: Bearer <CRM_ACCESS_TOKEN>
Content-Type: application/json

The examples use:

text
CRM_BASE_URL=https://crm.example.com

Replace it with the URL of your environment.

3. Data to Collect on the Site

Lead data

For creating a lead, the API supports:

FieldRequiredDescription
messagenoMessage or text of the inquiry from the form
ownerIdyesUUID of the CRM user responsible for the lead
authorIdyesUUID of the CRM user who created the lead
clientIdnoUUID of an existing client
contactIdnoUUID of an existing contact
leadSourceIdnoUUID of a node in the lead-source tree
statusIdnoUUID of the initial status
qualitynoHOT, WARM, COOL or COLD

ownerId and authorId are UUIDs of CRM users. For a public website form, do not let the visitor set these values themselves: the backend must supply them from the integration configuration.

Marketing visit data

Before the form is submitted, store the following in cookies, session storage, or on the backend:

Save the values on the first visit and do not overwrite them with empty parameters while navigating within the site.

4. Creating a Lead

Endpoint

http
POST /api/leads/create

Example request

bash
curl -X POST "$CRM_BASE_URL/api/leads/create" \
  -H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Website inquiry: consultation request",
    "ownerId": "11111111-1111-1111-1111-111111111111",
    "authorId": "11111111-1111-1111-1111-111111111111",
    "quality": "WARM"
  }'

Example 201 Created response

json
{
  "id": "22222222-2222-2222-2222-222222222222",
  "message": "Website inquiry: consultation request",
  "quality": "WARM",
  "dealId": null,
  "createdAt": "2026-09-05T20:00:00Z",
  "updatedAt": "2026-09-05T20:00:00Z",
  "version": 0,
  "author": {
    "id": "11111111-1111-1111-1111-111111111111",
    "firstName": "CRM",
    "lastName": "Bot"
  },
  "owner": {
    "id": "11111111-1111-1111-1111-111111111111",
    "firstName": "CRM",
    "lastName": "Bot"
  }
}

Save the response id: this value is passed in the leadId field of the marketing event.

5. Sending the Lead-Submitted Event

Endpoint

http
POST /api/dashboard/marketing/events

Example request

bash
curl -X POST "$CRM_BASE_URL/api/dashboard/marketing/events" \
  -H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "lead_submitted",
    "occurredAt": "2026-09-05T20:00:00Z",
    "visitorId": "visitor-8b7f",
    "sessionId": "session-31ac",
    "leadId": "22222222-2222-2222-2222-222222222222",
    "externalId": "site-form-submit-20260905-000123",
    "source": "website",
    "channel": "paid",
    "utmSource": "google",
    "utmMedium": "cpc",
    "utmCampaign": "summer-consulting",
    "utmContent": "banner-a",
    "utmTerm": "crm consultation",
    "gclid": "EAIaIQobChMI-example",
    "landingUrl": "https://www.example.com/consultation",
    "referrer": "https://www.google.com/",
    "metadata": {
      "formName": "consultation",
      "pageType": "landing"
    }
  }'

Example response

json
{
  "accepted": true,
  "duplicate": false,
  "eventId": "33333333-3333-3333-3333-333333333333"
}

If the same externalId has already been processed for this source and tenant pair, the API returns a success response with duplicate: true. Treat this as a success, not an error, and do not create a second lead.

6. Server-Side Handler Example

Below is a simplified Node.js example. Form data must go to the site backend, not directly to CRM from the browser.

js
async function createWebsiteLead(form, attribution) {
  const headers = {
    Authorization: `Bearer ${process.env.CRM_ACCESS_TOKEN}`,
    'Content-Type': 'application/json',
  };

  const leadResponse = await fetch(
    `${process.env.CRM_BASE_URL}/api/leads/create`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify({
        message: form.message || `Website inquiry: ${form.subject || 'no subject'}`,
        ownerId: process.env.CRM_LEAD_OWNER_ID,
        authorId: process.env.CRM_LEAD_AUTHOR_ID,
        quality: 'WARM',
      }),
    },
  );

  if (!leadResponse.ok) {
    throw new Error(`CRM lead creation failed: ${leadResponse.status}`);
  }

  const lead = await leadResponse.json();
  const externalId = `website-${form.requestId}`;

  const eventResponse = await fetch(
    `${process.env.CRM_BASE_URL}/api/dashboard/marketing/events`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify({
        eventType: 'lead_submitted',
        occurredAt: new Date().toISOString(),
        visitorId: attribution.visitorId,
        sessionId: attribution.sessionId,
        leadId: lead.id,
        externalId,
        source: 'website',
        channel: attribution.channel || 'direct',
        utmSource: attribution.utmSource,
        utmMedium: attribution.utmMedium,
        utmCampaign: attribution.utmCampaign,
        utmContent: attribution.utmContent,
        utmTerm: attribution.utmTerm,
        gclid: attribution.gclid,
        fbclid: attribution.fbclid,
        landingUrl: attribution.landingUrl,
        referrer: attribution.referrer,
        metadata: { formName: form.formName || 'website' },
      }),
    },
  );

  if (!eventResponse.ok) {
    // The lead is already created. Queue the event and retry it
    // with the same externalId instead of creating a second lead.
    throw new Error(`CRM marketing event failed: ${eventResponse.status}`);
  }

  return { leadId: lead.id, marketingEvent: await eventResponse.json() };
}

7. Idempotency and Retries

8. Personal Data and metadata

Passing personal data and form contents in metadata is forbidden:

Personal data must be passed only in the CRM entities intended for it — for example, in a pre-created contactId/clientId. Keep only technical attributes in metadata: form name, page type, banner variant, and so on.

9. Typical Responses and Errors

HTTPSituationWhat to do
201Lead createdSave the id and send lead_submitted
200 + duplicate: falseEvent acceptedSave the eventId in the integration log
200 + duplicate: trueEvent was already acceptedTreat as processed; do not send again
400Invalid data or PII in metadataFix the payload; retrying without changes will not help
401Invalid or missing tokenRefresh the CRM token on the backend
403Token lacks permission for the operationCheck role and tenant
5xx / timeoutTransient CRM or network errorRetry with the same externalId; for leads use a queue

10. Verifying Results in CRM

After integration, verify:

  1. The lead appears via GET /api/leads/{id} or in the list GET /api/leads/paged.
  2. The lead's owner, author, and creation time are correct.
  3. The marketing-event response has duplicate equal to false on the first send.
  4. Re-sending the same submission returns duplicate: true.
  5. In the marketing dashboard, the data appears in the right period, channel, and campaign.

Fetching the created lead

bash
curl "$CRM_BASE_URL/api/leads/$LEAD_ID" \
  -H "Authorization: Bearer $CRM_ACCESS_TOKEN"

Fetching leads for a list or reconciliation

bash
curl "$CRM_BASE_URL/api/leads/paged?page=0&size=20&createdAtFrom=2026-09-05T00:00:00Z&createdAtTo=2026-09-05T23:59:59Z&sort=createdAt,desc" \
  -H "Authorization: Bearer $CRM_ACCESS_TOKEN"

The page parameter starts at 0; the default size is 20. To fetch website leads, additionally use the message, createdAtFrom/createdAtTo, contactEmail, or search filters if the relevant data is already stored in CRM.