Integration recipe

Formgong + KeyCRM: form leads as pipeline cards

KeyCRM accepts new leads through its API with your own key. Formgong cannot add that key to a webhook. So a small step sits in between: a Make scenario, an n8n workflow or a script you host.

Short answer: Generate an API key in KeyCRM under Settings → General. Catch the Formgong webhook in Make or n8n, then post to https://openapi.keycrm.app/v1/pipelines/cards with Authorization: Bearer <key> and a contact object. KeyCRM allows 20 requests a minute per key.

Why there is a step in between

KeyCRM's API needs an Authorization: Bearer header and its own JSON shape. Formgong webhooks send a fixed JSON body and cannot add custom headers. So a webhook pointed straight at KeyCRM would fail with 401.

  • Make: free to start, no code. Custom webhook → HTTP Make a request.
  • n8n: Webhook node → HTTP Request node. Good if you self-host.
  • Your own script: a Cloudflare Worker or any HTTPS endpoint. The code is at the end of this page.

Prepare KeyCRM

  1. API key: Settings → General → API key → Generate API key. Only the CRM owner can refresh it. A refreshed key breaks every integration that used the old one.
  2. Source: add a source such as Website form, so you can see where leads come from. GET /order/source lists source IDs.
  3. Pipeline: GET /pipelines lists pipeline IDs. Without pipeline_id, KeyCRM uses the first pipeline.

Lead or order? Use a pipeline card (POST /pipelines/cards) for enquiries; it needs only a contact. Use an order (POST /order) for a real purchase; it needs source_id and buyer.

Recipe with Make

  1. Set up the Custom webhook as in the Make recipe and send one real submission.
  2. Add a filter: event equal to submission.created. Test deliveries then never create cards.
  3. Add HTTP → Make a request. URL https://openapi.keycrm.app/v1/pipelines/cards, method POST.
  4. Authentication type API key: create a keychain that sends the header Authorization with the value Bearer plus your key. Add the header Accept: application/json.
  5. Body content type application/json. Choose input method Data structure: Make then escapes quotes and line breaks a visitor typed. Fill it as shown below.
  6. Set Parse response to Yes, run once and check the new card in KeyCRM.
Body (Make mapping)
{
  "title": "{{1.form.name}}: {{1.submission.fields.name}}",
  "source_id": 12,
  "pipeline_id": 3,
  "contact": {
    "full_name": "{{1.submission.fields.name}}",
    "email": "{{1.submission.fields.email}}",
    "phone": "{{1.submission.fields.phone}}"
  },
  "manager_comment": "{{1.submission.fields.message}} | Page: {{1.submission.page_url}} | Formgong ID: {{1.submission.id}}",
  "utm_source": "{{1.submission.fields.utm_source}}"
}

Replace 12 and 3 with your source and pipeline IDs. Leave out keys you do not collect.

Recipe with n8n

  1. Set up the Webhook node as in the n8n recipe, with Only Run If {{ $json.body.event === 'submission.created' }}.
  2. Add an HTTP Request node: method POST, URL https://openapi.keycrm.app/v1/pipelines/cards.
  3. Authentication: Generic Credential Type → Header Auth, name Authorization, value Bearer plus your key.
  4. Turn on Send Body, content type JSON, Specify Body: Using JSON. JSON.stringify keeps quotes in messages from breaking the JSON.
  5. In the node settings, turn on Retry On Fail to ride out a 429.
Body (n8n expression)
{
  "title": {{ JSON.stringify($json.body.form.name + ": " + ($json.body.submission.fields.name ?? "")) }},
  "source_id": 12,
  "pipeline_id": 3,
  "contact": {
    "full_name": {{ JSON.stringify($json.body.submission.fields.name ?? "") }},
    "email": {{ JSON.stringify($json.body.submission.fields.email ?? "") }},
    "phone": {{ JSON.stringify($json.body.submission.fields.phone ?? "") }}
  },
  "manager_comment": {{ JSON.stringify(($json.body.submission.fields.message ?? "") + " | Page: " + ($json.body.submission.page_url ?? "") + " | Formgong ID: " + $json.body.submission.id) }},
  "utm_source": {{ JSON.stringify($json.body.submission.fields.utm_source ?? "") }}
}

Field mapping

FormgongKeyCRM card
fields.namecontact.full_name
fields.emailcontact.email
fields.phonecontact.phone (international format with +)
fields.message, page_url, submission.idmanager_comment
fields.utm_source … utm_contentutm_source … utm_content
Anything elsecustom_fields with the field's uuid from GET /custom-fields

UTM values reach the webhook only if your form posts them as normal fields, for example hidden inputs named utm_source. KeyCRM stores times in UTC, and so does Formgong's created_at.

Orders instead of cards

For an order form, post to /order. Put submission.id in source_uuid, the order number in the source. That makes a repeated delivery easy to find.

HTTP
POST https://openapi.keycrm.app/v1/order
Authorization: Bearer <your KeyCRM API key>
Content-Type: application/json
Accept: application/json

{
  "source_id": 12,
  "source_uuid": "<submission.id>",
  "buyer": {
    "full_name": "<fields.name>",
    "email": "<fields.email>",
    "phone": "<fields.phone>"
  },
  "buyer_comment": "<fields.message>",
  "products": [
    { "sku": "<fields.sku>", "name": "<fields.product>", "price": 0, "quantity": 1 }
  ]
}

Troubleshooting

  • 401: the key is wrong or Bearer is missing. A refreshed key must be updated in Make or n8n.
  • 422: a required field is missing, such as contact on a card or source_id on an order. Check the response body.
  • 429 Too Many Requests: more than 20 requests a minute per key. KeyCRM may block API access after repeated overuse, so keep other integrations in mind.
  • Same lead twice: Formgong retried after a slow or failed answer. Search by source_uuid or the ID in the comment before creating a new record.
  • Cards from tests: add the event filter from the steps above.

Without Make or n8n: a small script

If you prefer no extra tool, host a tiny endpoint. It checks Formgong's signature, builds the KeyCRM body and returns an error when KeyCRM fails, so Formgong retries.

Cloudflare Worker (JavaScript)
// Env: FORMGONG_SECRET (signing secret), KEYCRM_KEY (KeyCRM API key)
export default {
  async fetch(request, env) {
    if (request.method !== "POST") return new Response("POST only", { status: 405 });
    const raw = await request.text();
    const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(env.FORMGONG_SECRET),
      { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
    const mac = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(raw));
    const hex = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
    if (request.headers.get("x-signature") !== "sha256=" + hex) return new Response("bad signature", { status: 401 });
    const data = JSON.parse(raw);
    if (data.event !== "submission.created") return new Response("ignored");
    const f = data.submission.fields;
    const res = await fetch("https://openapi.keycrm.app/v1/pipelines/cards", {
      method: "POST",
      headers: { authorization: "Bearer " + env.KEYCRM_KEY, "content-type": "application/json", accept: "application/json" },
      body: JSON.stringify({
        title: data.form.name + ": " + (f.name ?? ""),
        source_id: 12,
        contact: { full_name: f.name ?? "", email: f.email ?? "", phone: f.phone ?? "" },
        manager_comment: (f.message ?? "") + " | Formgong ID: " + data.submission.id,
      }),
    });
    // A non-2xx answer makes Formgong retry (30 s, 2 min, 10 min, 30 min).
    return new Response(res.ok ? "ok" : "keycrm " + res.status, { status: res.ok ? 200 : 502 });
  },
};

Questions

Can Formgong send leads to KeyCRM without Make or n8n?

Not directly. KeyCRM needs a Bearer key header and its own JSON, and Formgong webhooks cannot add headers or reshape the body. A small script you host works as the middle step too.

Should I create a card or an order?

A card for enquiries and quote requests; it needs only a contact. An order when the form is a purchase with products; it needs a source and a buyer.

Where does the API key live?

In the Make keychain or the n8n credential, never in your website code. Formgong never sees it.

Will I still get the Telegram message?

Yes. The webhook runs alongside email and Telegram. Your team sees the lead in Telegram at once, and the card appears in KeyCRM a moment later.

Sources

Checked on 03.10.2026 against these public pages: KeyCRM OpenAPI documentation, KeyCRM Help: where to get an API key, Make: HTTP app, n8n docs: HTTP Request node, Formgong webhook reference.

← Formgong