JackAI NexusDocs
DocumentationLead Webhooks

Lead Webhooks

Send every new and updated lead from JackAI Nexus straight to your own CRM — Bitrix24, Zoho, HubSpot, n8n, Make, Zapier or your own server — with signed, retried, verifiable delivery.

Lead Webhooks

Push every new and updated lead from JackAI Nexus straight into your own CRM — Bitrix24, Zoho, HubSpot, Salesforce, an automation tool like n8n, Make or Zapier, or your own server.

Instead of asking your team to copy leads by hand, you register one URL. From that moment on, every time a lead is created or changes in Nexus, we send it to you automatically — usually within about a minute.


Quick Start

1. Get a URL from your CRM

Most tools give you a "webhook" or "incoming request" URL:

  • Zoho Flow — create a Flow, pick Webhook as the trigger, copy the URL it shows you
  • n8n — add a Webhook node and copy its Production URL
  • Make / Zapier — create a scenario or Zap starting with Webhook / Catch Hook
  • Your own server — any HTTPS endpoint that accepts a POST with a JSON body
  • Bitrix24 — connects directly, no automation tool needed. See Bitrix24 below

2. Register it in Nexus

  1. Go to Settings > Webhooks
  2. Click Add endpoint
  3. Fill in:
    • Name — anything that helps you recognize it, e.g. Zoho Sync
    • Endpoint URL — the URL you copied in step 1
    • Events — which changes you want to receive (see Events below)
  4. Click Create

3. Send a test

Open the endpoint you just created and click Send test. Nexus immediately sends a sample lead and shows you exactly what your server replied — status code, response body and how long it took.

If you see a green result, you are done. Your CRM will now receive real leads as they happen.


Bitrix24

Using Bitrix24? Nexus writes every lead straight into your Bitrix24 CRM. You don't need Zapier, Make, n8n or a developer.

  1. In Bitrix24, open Developer resources > Other > Inbound webhook
  2. Under Assign permissions, add CRM (crm)
  3. Click Save, then copy the Webhook to call REST API link. It looks like https://yourcompany.bitrix24.com/rest/1/abc123xyz/

Treat this link like a password. Anyone who has it can add and change records in your CRM. Don't paste it in chats or screenshots. If it leaks, click Generate new in Bitrix24.

2. Add it in Nexus

  1. Go to Settings > Webhooks and click Add endpoint
  2. Under Send leads to, choose Bitrix24
  3. Paste the link and click Create

The secret part of the link is hidden as soon as you save it.

3. Test the connection

Click Test connection. This only reads from Bitrix24 and never creates a lead.

You seeWhat to do
ConnectedNothing. New leads will start arriving
No CRM permissionIn Bitrix24, edit the webhook, add CRM (crm) under permissions, and save
Bitrix24 rejected the linkThe link is wrong or was deleted. Copy it again from Bitrix24 and click Change link
Simple CRM modeYour Bitrix24 has leads turned off, so each lead becomes a deal automatically

What arrives in Bitrix24

Bitrix24 fieldFrom Nexus
TitleThe customer's name, followed by "— JackAI"
NameThe customer's name
PhoneThe number in international form, e.g. +966501234567
EmailThe customer's email
CountryThe two-letter country code, e.g. SA
CommentsWhat the customer is asking for (the AI summary), their interests, your custom fields, status, score and assigned agent
Source descriptionJackAI Nexus
  • Changes update the same lead. When a phone or email changes, Bitrix24 shows the new value instead of a second one.
  • Nothing is ever deleted in Bitrix24. If your team deletes a lead in Bitrix24, Nexus leaves it deleted and does not add it back.
  • Leads that existed before you connected reach Bitrix24 the first time they change.
  • If you generate a new link in Bitrix24, open the endpoint and click Change link. Leads already sent stay linked as long as it's the same Bitrix24 account.

Bitrix24 receives new leads (lead.created) and their updates (lead.updated). The signature section below doesn't apply to Bitrix24, because the secret link itself authorizes each request.


Events

Choose which changes you want to receive. Most integrations subscribe to lead.created and lead.updated.

EventWhen it fires
lead.createdA new lead enters Nexus — from a conversation, an import, or created by hand
lead.updatedA lead's details change — score, status, name, email, assigned agent, custom fields
lead.deletedA lead was deleted
lead.mergedTwo leads turned out to be the same person and were merged into one

About lead.merged

Nexus recognizes the same customer across WhatsApp, Messenger, Instagram and web chat by phone number. When it discovers that two leads are the same person, it merges them and sends lead.merged. The payload carries the id that disappeared plus a merged_into field with the id that survived, so you can merge the matching records on your side too.


What we send

Every request is a POST with a JSON body in this shape:

{
  "type": "lead.updated",
  "timestamp": "2026-08-10T14:03:22+00:00",
  "data": {
    "id": "6a0e44b1-6d3c-4f9a-9b2e-2f1c8a7d4e10",
    "name": "Ahmad Al-Sayed",
    "phone": "966501234567",
    "phone_e164": "+966501234567",
    "email": "ahmad@example.com",
    "country": "SA",
    "status": "interested",
    "score": 72,
    "summary": "Asking about 3-bedroom units in the Yasmin project.",
    "interests": ["apartments", "yasmin-project"],
    "source": "conversation",
    "pipeline_excluded": false,
    "whatsapp_optout": false,
    "custom_fields": { "project_interest": "Yasmin" },
    "assigned_to": {
      "id": "7c1f...",
      "name": "Sara Ahmed",
      "email": "sara@yourcompany.com"
    },
    "created_at": "2026-08-10T13:58:02+00:00",
    "updated_at": "2026-08-10T14:03:20+00:00",
    "changed_fields": ["status", "score"]
  }
}

Fields

FieldMeaning
idThe lead's permanent id in Nexus. Store it — it is how you match updates to the record you already created
name, emailMay be empty until the customer provides them
phoneDigits only, no + or 00 — e.g. 966501234567
phone_e164The same number in +966501234567 form, if available
statusThe lead status from Settings > Lead statuses — your own configured values
score0–100, set by the AI from the conversation
summaryA short AI summary of what the customer wants
sourceconversation, import or manual
custom_fieldsYour organization's custom fields
assigned_toThe agent handling this lead, or null
changed_fieldsOn lead.updated only — which fields changed. Useful if you only care about some of them

Note: the payload is a snapshot of the lead at the moment we sent it. For the authoritative current state, treat the Nexus dashboard as the source of truth.


Delivery rules

Two rules to build against. Getting these right prevents duplicate and out-of-order records in your CRM.

At least once

We guarantee a webhook arrives at least once — occasionally the same event arrives twice (for example if your server received it but the reply was lost on the way back).

What to do: every request has a webhook-id header that stays the same across retries. Keep a note of the ids you have already processed and ignore repeats.

Order is not guaranteed

Two changes to the same lead may arrive in the opposite order.

What to do: compare the timestamp in the body and keep the newest. Do not assume the last request to arrive is the most recent change.

Grouped updates

While a customer is chatting, the AI may update the lead many times in a few seconds. We deliberately group those into a single lead.updated rather than sending dozens — so your CRM is not flooded and your rate limits are not burned. changed_fields lists everything that changed in the group.


Verifying the signature

Every request is signed so you can prove it came from Nexus and not from someone else who guessed your URL.

We follow the Standard Webhooks specification, which means you can verify with an off-the-shelf library instead of writing the logic yourself.

The headers

webhook-id: evt_9f3a1c4b8d2e4f6a9c1b3d5e7f2a4c6b
webhook-timestamp: 1786370640
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
pip install standardwebhooks
from standardwebhooks import Webhook

# From Settings > Webhooks — store it as a secret, never in your source code
secret = "whsec_..."

def handle_request(headers, raw_body: bytes):
    wh = Webhook(secret)
    payload = wh.verify(raw_body, headers)   # raises if the signature is invalid
    lead = payload["data"]
    print(lead["id"], lead["status"], lead["score"])
    return "", 200

JavaScript works the same way:

npm install standardwebhooks
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.NEXUS_WEBHOOK_SECRET);
const payload = wh.verify(rawBody, headers);   // throws if invalid

Verifying manually

If you prefer not to add a dependency, the signature is an HMAC-SHA256 of the id, timestamp and raw body joined by full stops:

import base64, hashlib, hmac

def is_valid(secret: str, headers, raw_body: bytes) -> bool:
    msg_id = headers["webhook-id"]
    timestamp = headers["webhook-timestamp"]
    signed_content = f"{msg_id}.{timestamp}.{raw_body.decode()}".encode()

    key = base64.b64decode(secret.removeprefix("whsec_"))
    expected = base64.b64encode(
        hmac.new(key, signed_content, hashlib.sha256).digest()
    ).decode()

    # The header may carry several space-separated signatures during rotation
    for entry in headers["webhook-signature"].split(" "):
        version, _, signature = entry.partition(",")
        if version == "v1" and hmac.compare_digest(signature, expected):
            return True
    return False

Two things to get right:

  • Sign the raw request body, exactly as received. If your framework parses the JSON and you re-serialize it, the bytes change and the signature will never match.
  • Use a constant-time comparison (hmac.compare_digest), not ==.

Rejecting replays

Also check that webhook-timestamp is within a few minutes of your current time — we recommend 5 minutes. This stops someone from capturing a valid request and replaying it later.


Rotating the secret

If your secret is ever exposed, open the endpoint and click Rotate secret.

The old secret keeps working for 24 hours so you have time to deploy the new one without missing any leads. During that window we sign each request with both, and the webhook-signature header carries two space-separated signatures — the verification libraries handle this automatically.


Responding correctly

Reply with a 2xx status as soon as you have safely stored the event. Do the slow work — writing to your CRM, sending emails — after you reply.

@app.post("/nexus-webhook")
def nexus_webhook():
    payload = verify_and_parse(request)
    queue.enqueue(process_lead, payload)   # do the real work in the background
    return "", 200                          # reply immediately

Requests time out after 10 seconds. A slow endpoint looks like a failed one and gets retried unnecessarily.

Which replies mean what

Your replyWhat we do
2xxSuccess — done
429, 5xx, timeout, connection errorRetry with increasing delays for up to 24 hours
Other 4xx (400, 401, 404, 410)Treated as permanent — no retry. Use these only when you truly want us to stop
3xx redirectTreated as a failure. Register the final URL directly instead

When something goes wrong

Recent deliveries

Open your endpoint and click Show recent deliveries. Every attempt is listed with the status code, the response body we received and how long it took — usually enough to see the problem without touching your own logs.

Automatic retries

A failed delivery is retried with growing gaps (seconds, then minutes, then hours) for up to 24 hours. A brief outage on your side loses nothing.

Automatic disabling

If an endpoint keeps failing over a longer period, we disable it and email your organization's admins. Nothing is silently dropped — you always find out.

To bring it back: fix the endpoint, then open Settings > Webhooks and switch it back on. The failure counter resets.

Common causes

What you seeUsually means
404 on every attemptThe URL changed, or the scenario in your CRM was deleted
401 / 403Your tool expects an auth token the URL no longer carries
Signature never matchesYou are signing the parsed-and-re-serialized JSON instead of the raw body
The same lead arrives twiceNormal — dedupe on webhook-id
Fields look out of dateAn older event arrived late — compare timestamp and keep the newest

Security notes

  • Treat the signing secret like a password. Store it in your environment or secret manager, never in your source code.
  • HTTPS only. We do not deliver to plain http:// addresses.
  • Public addresses only. Endpoints on internal or private networks are rejected, in both directions.
  • Verify the signature. Your URL is not a secret — anyone who learns it could post to it. The signature is what proves a request really came from us.

Frequently asked questions

Do I need a developer? Not for the basics. Zoho Flow, n8n, Make and Zapier can receive the webhook and map the fields with no code. A developer is only needed for signature verification, which is optional with those tools since your webhook URL is already private.

Does this affect my customers or my AI replies? No. Webhooks only read what already happened. Registering one changes nothing about how the AI talks to customers.

Will it send my existing leads? No. Only changes from the moment you create the endpoint. Use Leads > Export for a one-off copy of what you already have.

Do you support Bitrix24? Yes, directly. Choose Bitrix24 when you add the endpoint and paste the inbound webhook link from Bitrix24. See Bitrix24.

Can I send to more than one system? Yes. Add an endpoint for each. They are delivered independently, so one being down does not affect the others.

How fast is it? Normally within about a minute of the change. Updates during an active conversation are grouped, so they arrive slightly later but as a single, complete update.