Typeform responses can become leads in GuardenerAI through the public API, with one step in between. This is a connection you build, not a native integration, and Typeform webhooks cannot add a custom header, so they cannot send your API key themselves. Route each response through Zapier, Make, n8n or a small function of your own, which searches GuardenerAI by email and then creates or updates the lead.
Not a native integration. Typeform sends each response to your middle step, which writes the lead into GuardenerAI. Nothing goes back to Typeform.
With Zapier, start from Typeform’s New Entry trigger. With Make, use Typeform’s Watch Responses. With n8n, point a Typeform webhook (Connect, Webhooks in Typeform) at an n8n Webhook node. Each of these then calls the GuardenerAI API with your key.
Typeform sends answers as a list under form_response.answers, each with its question’s field.ref and the value under a key named for its type, such as text or email. Match on the ref, so reordering the form does not break the mapping.
Search leads with filter=email:equals: and the lower-cased email. If a lead comes back, update it with PUT, sending the whole record; if not, create it with POST /api/leads. Typeform retries deliveries that fail or time out, so the search also stops a retried response from becoming a second lead.
If you would rather not use an automation tool, a function on any serverless host can take the webhook. Set a secret on the Typeform webhook and check the Typeform-Signature header before trusting the body: it is sha256= plus the base64 HMAC-SHA256 of the raw body, keyed with your secret.
import crypto from 'node:crypto';
const API = 'https://YOUR-ORG.guardenerai.com/api/leads';
const H = { 'X-Api-Key': process.env.GUARDENERAI_KEY, 'Content-Type': 'application/json' };
export async function handle(rawBody, signature) {
const expected = 'sha256=' + crypto.createHmac('sha256', process.env.TYPEFORM_SECRET)
.update(rawBody).digest('base64');
if (signature !== expected) return 401;
const answers = JSON.parse(rawBody).form_response.answers;
const by = (ref) => answers.find((a) => a.field.ref === ref);
const lead = {
firstName: by('first_name')?.text,
lastName: by('last_name')?.text,
email: by('email')?.email.trim().toLowerCase(),
message: by('project')?.text,
sourceMedium: 'typeform'
};
const found = await (await fetch(API + '?filter=email:equals:' + encodeURIComponent(lead.email), { headers: H })).json();
const res = found.totalElements > 0
? await fetch(API + '/' + found.content[0].id, { method: 'PUT', headers: H, body: JSON.stringify({ ...found.content[0], ...lead }) })
: await fetch(API, { method: 'POST', headers: H, body: JSON.stringify(lead) });
return res.status;
} message or in a custom field your admin has created.Retry-After: 1, so wait a second and retry.PUT replaces the whole record. Send every field you want to keep, not only the one that changed.duplicate_blocked with the matching records. Update the match instead of forcing a second copy.Checked against Typeform’s own documentation: Typeform webhooks, Webhook payload, Securing webhooks, Typeform on Zapier, Typeform on Make. Their plans and features change, so confirm the current details there.
We use analytics cookies to see which pages help and which don't. No advertising cookies. How we use them