AI integration in amoCRM for auto-replies and lead analysis

A new lead in amoCRM sends a webhook to your server, the model assesses the request, and a hint for the manager appears as a note on the card.

4 min · · бизнес · автоматизация

Managers in amoCRM spend a noticeable part of the day reading new requests, judging their urgency, and deciding what to do next. An AI model can do the first pass for them: amoCRM tells your server about a new lead, the server sends its description to a model via Zukko, and the answer appears as a note on the card. Below are the webhook setup, an API token, a working PHP handler, and a cost estimate.

How the amoCRM AI integration works

  1. A lead is created in amoCRM → a webhook sends its data to your URL.
  2. The handler builds the text for analysis from the lead fields.
  3. The text goes to the model via https://api.zukko.pro/v1/chat/completions.
  4. The answer is written to the lead as a note through the amoCRM REST API (/api/v4/leads/notes).

In amoCRM, deals and leads are the same entity — leads in the API and webhooks.

Step 1: a webhook in amoCRM

According to the amoCRM docs (its international version is Kommo, which shares the API), webhooks are set up like this:

  1. Open Settings → Integrations and click Web hooks in the upper right corner.
  2. Enter the handler URL, for example https://your-server.com/amo-handler.php?key=long_random_string.
  3. Select the "Lead added" event and save.

Two details shape the code:

  • Data arrives not as JSON but as a regular form (application/x-www-form-urlencoded): leads[add][0][id], leads[add][0][name], leads[add][0][custom_fields][...]. PHP has already parsed it into $_POST.
  • amoCRM waits about 2 seconds for a response. If the handler is too slow or returns an error, the webhook counts as undelivered. So we answer 200 first and call the model afterwards.

CRM webhooks aren't signed, so add a secret key parameter to the URL and check it in the handler — otherwise anyone could send requests to your URL and spend your balance.

Step 2: a token for the amoCRM API

To write notes, the handler needs access to the account API. For an integration that only runs in your own account, a long-lived token is the simplest option:

  1. In Integrations, create your own (external) integration.
  2. On the keys and scopes tab, generate a long-lived token and choose its expiry.
  3. Store it in a server environment variable.

The token works with the rights of the administrator who created it, so treat it like a password. Exact button names depend on the interface version — check the amoCRM developer docs. If the integration will be installed in other people's accounts, you need full OAuth authorization with token refresh.

Step 3: a PHP handler

bash
<?php
// amo-handler.php — amoCRM "Lead added" webhook handler

if (!hash_equals((string) getenv('AMO_HOOK_KEY'), (string) ($_GET['key'] ?? ''))) {
    http_response_code(403);
    exit;
}

// amoCRM waits no more than ~2 seconds: respond right away (PHP-FPM)
http_response_code(200);
echo 'OK';
if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();
}

Environment variables: ZUKKO_API_KEY, AMO_SUBDOMAIN, AMO_TOKEN, and AMO_HOOK_KEY (the secret string from the webhook URL). Replace the field names in ALLOWED_FIELDS with your own: this way only data needed for the assessment goes to the model, without customer contacts. For a Kommo account, the API host is your_account.kommo.com.

If PHP doesn't run under FPM and fastcgi_finish_request isn't available, save incoming events to a database or file and process them with a separate cron script.

A task instead of a note

For urgent leads it's handier to create a task for the responsible user right away. That's the POST /api/v4/tasks method:

bash
function addTask(int $leadId, string $text): void
{
    $subdomain = getenv('AMO_SUBDOMAIN');
    $ch = curl_init("https://{$subdomain}.amocrm.ru/api/v4/tasks");
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode([[
            'entity_id'     => $leadId,
            'entity_type'   => 'leads',
            'text'          => $text,
            'complete_till' => time() + 3600, // due in one hour
        ]], JSON_UNESCAPED_UNICODE),
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . getenv('AMO_TOKEN'),
            'Content-Type: application/json',
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 15,
    ]);
    curl_exec($ch);
}

To decide when to create a task, ask the model to start its answer with "HIGH", "MEDIUM", or "LOW" and check the first word in code.

What it costs

Example: 50 leads a day, about 300 input and 100 output tokens each. Per day that's 50 × 400 = 20,000 tokens, or 0.02M.

ModelPrice in/out per 1MPer dayPer 30 days
glm-5.3-flash$0.04 / $0.040.02 × 0.04 = $0.0008$0.024
gpt-5.6-luna$0.1 / $0.10.02 × 0.1 = $0.002$0.06
claude-sonnet-5$0.3 / $0.30.02 × 0.3 = $0.006$0.18

At 500 leads a day, multiply by 10: on glm-5.3-flash that's $0.24 per 30 days. Real token counts are in the response's usage field and in the spending history in the dashboard. How useful a model is for your particular requests is something to test on a sample: run 30–50 past leads with a known outcome through two or three models and compare. Request parameters are in the Chat Completions docs.

Personal data

Customer data ends up in the model request, so:

  1. Send only the fields needed for the assessment (the ALLOWED_FIELDS list in the code). Phone, email, and full name aren't needed to judge urgency.
  2. Don't write full lead data to logs — the ID and error code are enough.
  3. Make sure processing data with an external service doesn't conflict with your policy and your customers' consents.

FAQ

The webhook doesn't arrive — what should I check?

  1. The webhook is saved and the right event is selected.
  2. The URL is reachable from the internet over HTTPS.
  3. The handler responds with 200 in under 2 seconds. For debugging, temporarily log incoming data to a file: file_put_contents('/tmp/amo.log', print_r($_POST, true), FILE_APPEND);

How do I check that the token works?

Request the list of leads:

bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://your_account.amocrm.ru/api/v4/leads?limit=1"

A response with data means the token is valid; 401 means it's wrong or expired.

Notes aren't being added — why?

Check error_log: amoCRM returns an error code and description. Common causes are a wrong subdomain, an expired token, or a lead ID the token owner can't access.

Where do I get a Zukko key, and how do I cap spending?

Create a separate key for amoCRM in the dashboard, set a daily limit, and allow only the model you need — then an accidental loop in the handler can't overspend. More in the key limits section.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

amoCRM AI integration: lead analysis via webhooks and API · Zukko