Each business can have inbound webhooks: secret web addresses that accept leads from any other tool.
Make one
Settings → Inbound webhooks → New webhook. Give it a name (for example "Facebook leads") and, optionally, tags to add to every contact from it. The web address is shown once: copy it into the other tool. Keep it secret; anyone with it can add contacts. New address replaces it (the old one stops working at once), and you can switch a webhook off.
What to send
Send a POST with a JSON body. These names are found on their own: email, phone, first_name, last_name or name, and company (common spellings like firstName and phone_number work too). For anything else, set which part of the data goes where in the webhook's settings, using a path like lead.contact.email or answers[0].value, including into custom fields.
curl -X POST "https://your-address/api/v1/hooks/…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: lead-12345" \
-d '{"email":"sam@example.com","first_name":"Sam","phone":"555 010 0100","budget":"2000"}'
The answer says whether a contact was made (201) or an existing one was updated (200).
What happens
The contact is found by email or phone (the business's duplicate rule), or made; empty fields are filled; a Data received by webhook entry with the data goes in the Activity history; the webhook's tags are added. UTM values sent as utm_source, utm_medium, utm_campaign, utm_term and utm_content (or inside a utm object), and a page_url, go on the contact's Where they came from.
Send an Idempotency-Key header (or an id / event_id in the body) and a delivery sent twice is only counted once, so a tool that retries never doubles an entry.
When something's wrong
- No email or phone in the data: send
emailorphone, or set which fields to use in the webhook settings. The last problem shows on the webhook in Settings. - This webhook is switched off: switch it back on.
- Too many requests from one place at once are slowed down (more than 120 a minute).