Dlaczego potrzebny jest pośrednik
API KeyCRM wymaga nagłówka Authorization: Bearer i własnej struktury JSON. Webhook Formgong ma stałą treść i nie dodaje własnych nagłówków. Webhook wysłany prosto do KeyCRM dostałby więc 401.
- Make: darmowy start, bez kodu. Custom webhook → HTTP Make a request.
- n8n: Webhook node → HTTP Request node. Wygodne, jeśli masz własny serwer.
- Własny skrypt: Cloudflare Worker albo dowolny adres HTTPS. Kod jest na końcu strony.
Przygotuj KeyCRM
- Klucz API: w ustawieniach ogólnych KeyCRM znajdź wiersz z kluczem API i wygeneruj klucz. KeyCRM ma polski interfejs, ale jego pomoc opisuje ścieżkę po ukraińsku: «Налаштування» → «Основні». Odnowić klucz może tylko właściciel CRM. Stary klucz przestaje wtedy działać we wszystkich integracjach.
- Źródło: dodaj źródło, np. „Formularz na stronie”, żeby widzieć, skąd przyszedł lead. ID źródeł zwraca
GET /order/source. - Lejek: ID lejków zwraca
GET /pipelines. Bezpipeline_idKeyCRM użyje pierwszego lejka z listy.
Karta czy zamówienie? Dla zapytań i wycen – karta w lejku (POST /pipelines/cards), wystarczy jej contact. Dla zakupu z produktami – zamówienie (POST /order), które wymaga source_id i buyer.
Przepis dla Make
- Skonfiguruj Custom webhook według poradnika Make i wyślij jedno prawdziwe zgłoszenie.
- Dodaj filtr:
eventrównesubmission.created. Doręczenia testowe nie utworzą wtedy kart. - Dodaj HTTP → Make a request. URL
https://openapi.keycrm.app/v1/pipelines/cards, metoda POST. - Authentication type: API key. Utwórz keychain, który wysyła nagłówek
Authorizationz wartościąBeareri Twoim kluczem. Dodaj nagłówekAccept: application/json. - Body content type: application/json, sposób wprowadzania Data structure. Make sam zadba o cudzysłowy i nowe linie wpisane przez klienta. Wypełnij jak poniżej.
- Parse response ustaw na Yes. Uruchom raz i sprawdź nową kartę w KeyCRM.
{
"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}} | Strona: {{1.submission.page_url}} | Formgong ID: {{1.submission.id}}",
"utm_source": "{{1.submission.fields.utm_source}}"
}Zamień 12 i 3 na ID swojego źródła i lejka. Usuń klucze, których nie zbierasz.
Przepis dla n8n
- Skonfiguruj Webhook node według poradnika n8n, z Only Run If
{{ $json.body.event === 'submission.created' }}. - Dodaj węzeł HTTP Request: metoda POST, URL
https://openapi.keycrm.app/v1/pipelines/cards. - Authentication: Generic Credential Type → Header Auth, nazwa
Authorization, wartośćBeareri Twój klucz. - Włącz Send Body, typ JSON, Specify Body: Using JSON.
JSON.stringifypilnuje, by cudzysłów w wiadomości nie zepsuł JSON. - W ustawieniach węzła włącz Retry On Fail, żeby przetrwać odpowiedź 429.
Treść (wyrażenie n8n)
{
"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 ?? "") + " | Strona: " + ($json.body.submission.page_url ?? "") + " | Formgong ID: " + $json.body.submission.id) }},
"utm_source": {{ JSON.stringify($json.body.submission.fields.utm_source ?? "") }}
}Mapowanie pól
| Formgong | Karta KeyCRM |
|---|---|
fields.name | contact.full_name |
fields.email | contact.email |
fields.phone | contact.phone (format międzynarodowy z +48) |
fields.message, page_url, submission.id | manager_comment |
fields.utm_source … utm_content | utm_source … utm_content |
| Wszystko inne | custom_fields z uuid pola z GET /custom-fields |
Parametry UTM trafią do webhooka tylko wtedy, gdy formularz wysyła je jako zwykłe pola, np. ukryte inputy o nazwie utm_source. KeyCRM zapisuje czas w UTC, tak samo jak pole created_at w Formgong.
Zamówienia zamiast kart
Dla formularza zamówienia wysyłaj żądanie na /order. Wpisz submission.id w source_uuid, czyli numer zamówienia w źródle. Dzięki temu łatwo znaleźć powtórne doręczenie.
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 }
]
}Gdy coś nie działa
- 401: zły klucz albo brak słowa
Bearer. Po odnowieniu klucza zmień go w Make lub n8n. - 422: brakuje wymaganego pola, np.
contactw karcie albosource_idw zamówieniu. Sprawdź treść odpowiedzi. - 429 Too Many Requests: ponad 20 żądań na minutę na klucz. Przy częstym przekraczaniu KeyCRM może zablokować dostęp do API, więc pamiętaj o innych integracjach.
- Ten sam lead dwa razy: Formgong ponowił żądanie po wolnej lub błędnej odpowiedzi. Przed utworzeniem rekordu szukaj po
source_uuidalbo ID w komentarzu. - Karty z testów: dodaj filtr na
eventz kroków powyżej.
Bez Make i n8n: mały skrypt
Jeśli nie chcesz dodatkowego narzędzia, postaw mały endpoint. Sprawdza podpis Formgong, buduje treść dla KeyCRM i zwraca błąd, gdy KeyCRM odrzuci żądanie. Wtedy Formgong ponowi próbę.
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 });
},
};Pytania i odpowiedzi
Czy Formgong wyśle leady do KeyCRM bez Make i n8n?
Nie bezpośrednio. KeyCRM potrzebuje nagłówka z kluczem Bearer i własnego JSON, a webhook Formgong nie dodaje nagłówków ani nie zmienia treści. Pośrednikiem może być też Twój mały skrypt.
Tworzyć kartę czy zamówienie?
Kartę dla zapytań i wycen, wystarczy jej kontakt. Zamówienie, gdy formularz to zakup z produktami; wtedy potrzebne są źródło i kupujący.
Gdzie trzymać klucz API?
W keychain Make albo w danych uwierzytelniających n8n, nigdy w kodzie strony. Formgong go nie widzi.
Czy wiadomość w Telegramie też przyjdzie?
Tak. Webhook działa równolegle z e-mailem i Telegramem. Zespół widzi zgłoszenie w Telegramie od razu, a karta pojawia się w KeyCRM chwilę później.
Źródła
Sprawdzone 03.10.2026 w oficjalnej pomocy i na publicznych stronach: Dokumentacja OpenAPI KeyCRM, Pomoc KeyCRM: gdzie wziąć klucz API, Make: aplikacja HTTP, Dokumentacja n8n: HTTP Request node, Dokumentacja webhooków Formgong.