Discuss your task

Kommo + Airtable: deal sync without Zapier

Synchronization between Kommo and Airtable works through a direct connection: a Kommo webhook on deal status change -> your middleware -> Airtable REST API (POST/PATCH /v0/{baseId}/{tableIdOrName}/records). The reverse direction - Airtable Automation -> your endpoint -> Kommo API PATCH /api/v4/leads/{id}. There is no native two-way integration between these platforms; no-code bridges like Zapier or Albato do not provide the flexibility needed at 500+ deals per month.

The operations team maintains a project database in Airtable: deadlines, owners, onboarding statuses. Sales close deals in Kommo. At some point the data diverges: a manager moves a deal to “Won” - the operations person does not see it until someone manually updates the row in Airtable. Or the reverse: a client signed an act in Airtable - the salesperson finds out via messenger, not from the CRM.

In Exceltic.dev projects this scenario appears consistently at companies of 15+ people where sales and operations are tracked in different tools. This article describes the concrete architecture for two-way sync and the real API parameters you need to know before starting development.

Why there is no native integration

Kommo does not offer a built-in connector to Airtable. The Airtable Marketplace has integrations with Salesforce and HubSpot, but not with Kommo. This is not accidental: Airtable positions itself as a flexible database, not a CRM ecosystem, so partner integrations target the enterprise stack.

But the main problem is not the missing connector. The problem is that even if it existed, it would not cover a two-way scenario with configurable logic:

  • Field mapping between Kommo (custom_fields_values) and Airtable (fields object) is non-trivial: data types, pipeline stage enum values, multi-selects - all of this requires conversion.
  • Zapier and Make pass data via polling or a simple webhook, but do not support idempotent upsert operations by an external key. On failure or duplicate event, Airtable creates a duplicate record.
  • Business logic is often more complex than “field A -> field B”: you need to update Airtable only on certain statuses, enrich the record with data from a third source, or skip test deals.

Upsert - an operation that updates a record if it exists, or creates a new one if it does not. In the Airtable API this is the performUpsert parameter with fieldsToMergeOn - the key field for idempotency.

What the integration handles

Kommo webhook -> your endpoint -> Airtable API upsert by external_id

Kommo sends a webhook when a deal stage changes (leads.status in the payload). Your middleware accepts the POST in x-www-form-urlencoded format, extracts the deal id, requests the full data via GET /api/v4/leads/{id}, builds a fields object for Airtable, and calls PATCH https://api.airtable.com/v0/{baseId}/{tableIdOrName}/records with the performUpsert parameter.

The idempotency key is kommo_deal_id, stored in Airtable as a plain numeric field. In the request:

{
  "records": [
    {
      "fields": {
        "Kommo Deal ID": 123456,
        "Deal Name": "Acme Corp - Enterprise",
        "Stage": "Negotiation",
        "Amount": 15000,
        "Manager": "Ivan Petrov"
      }
    }
  ],
  "performUpsert": {
    "fieldsToMergeOn": ["Kommo Deal ID"]
  }
}

Airtable returns createdRecords or updatedRecords in the response - you can use this to log the result. Authorization: Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN header. Rate limit: 5 requests per second per base; on exceeding it you get HTTP 429 and need exponential backoff with a pause of 30+ seconds.

The Kommo webhook is configured in Settings -> Integrations -> Web hooks. Select the “Lead stage (status) changed” event and provide your endpoint URL. The payload arrives as x-www-form-urlencoded and must be handled synchronously - respond within 2 seconds, otherwise Kommo will retry (up to 5 times).

Reverse sync: Airtable Automation -> your endpoint -> Kommo API deal update

Airtable supports built-in Automations: a trigger on field change + a Send HTTP request action. When the operations team updates a status in Airtable (for example, “Onboarding Complete”), the automation fires and sends JSON to your endpoint.

The endpoint receives the data, maps it to Kommo custom fields, and calls:

PATCH https://{subdomain}.kommo.com/api/v4/leads/{id}

With the body:

[
  {
    "id": 123456,
    "custom_fields_values": [
      {
        "field_id": 789012,
        "values": [{"value": "Onboarding Complete"}]
      }
    ]
  }
]

Kommo API uses OAuth 2.0 or a long-lived Integration Token. field_id is the numeric ID of the custom field, which must be retrieved in advance via GET /api/v4/leads/custom_fields.

Idempotency and duplicate protection

Both directions must be idempotent: a repeated call with the same data must not create a duplicate and must not produce an error.

For Airtable this is guaranteed by performUpsert - on a repeated webhook from Kommo the record is simply updated. For Kommo, a PATCH request on an existing id is also idempotent by nature.

Additional protection: store last_event_ts of the last processed event and skip events with an older timestamp. This closes the scenario where Kommo resends a webhook after a timeout.

Step-by-step flow

  1. Kommo webhook - Settings -> Integrations -> Web hooks -> “Lead stage (status) changed” event -> URL of your middleware
  2. Middleware receives payload - parses x-www-form-urlencoded, extracts leads[status][0][id] and leads[status][0][status_id]
  3. GET /api/v4/leads/{id} - fetches the full deal data: name, amount, owner, custom fields
  4. Field mapping - converts Kommo status_id to a string value for Airtable, numeric fields -> numbers, text -> text
  5. PATCH Airtable - https://api.airtable.com/v0/{baseId}/{tableId}/records with performUpsert on the Kommo Deal ID field. Authorization via Authorization: Bearer PAT_...
  6. Log the result - createdRecords vs updatedRecords in the Airtable response, retry on HTTP 429
  7. Airtable Automation - trigger on the relevant field change -> Send HTTP request -> your endpoint
  8. Reverse mapping - endpoint receives Airtable payload, builds custom_fields_values for Kommo
  9. PATCH /api/v4/leads/{id} - updates the deal in Kommo by kommo_deal_id from Airtable
  10. Deduplication - timestamp check, ignoring stale events

Infrastructure: any lightweight HTTP server (Python/FastAPI, Node.js/Express, Go). Hosting - Railway, Render, or a Lambda function. Data volume is small - 1 request per event; at 100 deals per day that is about 200 API calls total, well within rate limits.

Real-world case with numbers

A company of 22 people - sales work in Kommo, the operations team (onboarding, project management) runs Airtable. Before the integration: manual sync took 30-45 minutes per day for two employees, data transfer lag ranged from several hours to a full day, systematic errors in deal amounts (different input formats in the two systems).

After launching the custom sync through Exceltic.dev:

  • Delay between a status change in Kommo and the update appearing in Airtable - up to 5 seconds (webhook processing time + API call)
  • Manual sync time - 0 (the task no longer exists)
  • Duplicates during testing on 2000 events in the first month - 0, thanks to performUpsert and timestamp deduplication
  • Development and launch timeline - 3 weeks, including custom field mapping, Airtable Automation setup, and testing

A “filtering” requirement was also solved: test deals and deals tagged “internal” are not synced to Airtable - business logic that a no-code tool cannot implement without workarounds.

Who this integration is for

This architecture makes sense for companies where:

  • Sales are run in Kommo, operations (onboarding, delivery, project management) are in Airtable
  • Deal volume is 50+ per month, at which point manual sync is noticeably costly
  • Two-way connection is needed: Kommo must see updates from Airtable, and vice versa
  • Zapier or Make have already been tried but the cost at volume was too high, or duplicates and data loss occurred on failures

Less suitable if sync is needed in only one direction and without complex mapping - in that case Zapier may cover the need more cheaply.

Related scenarios we have implemented: Kommo + Google Sheets with the same upsert logic, and Kommo + Jira for passing tasks between teams. The core principle is the same across all of them - middleware with idempotent writes by external key.

If you want to understand what custom integration tools exist in the Kommo ecosystem in general, read the custom integrations for Kommo CRM overview - it covers patterns and limitations.

Term: Personal Access Token (PAT) - an authorization token for the Airtable API, replacing the old API Key. Created at airtable.com/account, passed in the Authorization: Bearer PAT_... header, has configurable scopes.

Frequently asked questions

Is a server required for this integration?

Yes, an intermediate endpoint - your middleware - is required. This does not have to be a full server: a Lambda function (AWS, Cloudflare Workers) or a simple app on Railway/Render works fine. The free tiers on these platforms are sufficient for 100-300 requests per day. A fully serverless approach works well: the function starts only on an incoming webhook, and the cost is near zero at moderate volumes.

What happens if Kommo sends a webhook and Airtable does not respond?

Kommo retries delivery up to 5 times with increasing intervals. If your endpoint did not respond with a 200-299 code within 2 seconds, Kommo will try again. On the Airtable side: if you receive HTTP 429 (exceeding the 5 req/s limit), implement exponential backoff - wait 30+ seconds before retrying. In a typical project, a queue of 10-20 events and retry logic with 3 attempts is sufficient.

Can you sync only specific deals rather than all of them?

Yes, this is a standard part of middleware logic. Kommo webhooks arrive for all deals in the account, but your code filters by pipeline_id, status_id, tags, or custom fields. For example, sync only deals from the “Enterprise” pipeline or only when transitioning to “Won” or “In Progress” statuses. This cannot be configured on the Kommo webhook side itself - only at the handler level.

How do you get the field_id of custom fields in Kommo?

Through the API: GET https://{subdomain}.kommo.com/api/v4/leads/custom_fields - returns a list of all custom fields with their id, name, and type. Do this once at the start of the project and lock down the mapping. Fields do not change ID when renamed, so the mapping is stable.

Does the Airtable API support attachments and linked records between tables?

Yes. Attachments (attachments) are passed as an array of objects with url. Linked records (linkedRecord) are passed as an array of record IDs from the linked table. For most Kommo sync scenarios these types are not needed: text fields, numbers, and single select are sufficient. If you need a link to a client table, sync the contact as a separate call and pass its record ID into the deal field.

Next steps

If you currently have:

  • Operations and sales working in separate tools with data diverging
  • Already tried Zapier/Make - too expensive at volume, or duplicates and losses on failures
  • Need two-way sync with non-standard filtering logic

Describe the problem to the Exceltic.dev team. We will review your stack: which fields need to sync, in which directions, what event volume to expect. We will estimate the scope of work and propose a concrete architecture.

Technical documentation if you want to explore on your own: Airtable Web API and Kommo Webhooks.

More articles

All →