# Lead capture The door a person comes in through when they raise their hand (U1-1). ## Capture a lead `POST /api/leads` Records that a person raised their hand, whether or not you have ever seen them before. Send what you have; the CRM decides what it means. **This endpoint never rejects a returning person.** Post the same email twice and you get the same contact back, with `contact_created: false` — where `POST /api/contacts` would answer `422` and drop everything you sent. Use this one for forms, ad callbacks and automations; use `/api/contacts` when you are deliberately writing a record. **Nothing you send creates schema.** A key in `custom_field_values` that does not match an existing custom field is not an error and does not create a field: it is kept on the touch and listed back to you in `unmapped_keys`, so a typo can never become a permanent column. **Existing data is not overwritten.** Each field applies its own write rule — most fill only when empty, attribution fields are written once and locked. Anything refused comes back in `skipped` with the reason, so you never have to guess whether your value landed. Repeat submissions of the same form by the same person, for the same thing, within 30 minutes count as one arrival. Send an `Idempotency-Key` header to make that guarantee explicit and unbounded in time — see [Idempotency](#idempotency). ### Body parameters - `email` (string) — The person's email. Required unless `phone` is given. - `phone` (string) — The person's phone. Required unless `email` is given. E.164 with the country code — `+525555555555`. A local number is only accepted together with `phone_country`; without either the request is rejected. - `phone_country` (string) — ISO-3166-1 alpha-2, e.g. `MX`. Only needed when `phone` has no `+` country code; ignored when it does. - `first_name` (string) - `last_name` (string) - `company_name` (string) - `timezone` (string) — IANA timezone. - `source` (string) — How they reached you, recorded once and never overwritten. - `interest` (string) — What they asked for, in their own words. Stored as text; matched to a product only if one by that name already exists. - `landing_page` (string) — The page they arrived on. - `referrer` (string) — The referring URL. - `utm` (object) — Campaign parameters. Also accepted flat at the top level. - `utm_source` (string) - `utm_medium` (string) - `utm_campaign` (string) - `utm_term` (string) — Must not be greater than 150 characters. - `utm_content` (string) — Must not be greater than 150 characters. - `gclid` (string) - `fbclid` (string) — Must not be greater than 255 characters. - `msclkid` (string) — Must not be greater than 255 characters. - `custom_field_values` (object) — Values keyed by custom field `key`. Unknown keys are kept, not created. ### Example request ```bash curl --request POST \ "https://klozzo.com/api/leads" \ --header "Authorization: Bearer {YOUR_API_TOKEN}" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"email\": \"ana.lopez@example.com\", \"phone\": \"+525555555555\", \"phone_country\": \"MX\", \"first_name\": \"Jonathan\", \"last_name\": \"Zapata\", \"company_name\": \"Klozzo\", \"timezone\": \"America\\/Mexico_City\", \"source\": \"landing-verano\", \"interest\": \"Paquete de firmas\", \"landing_page\": \"https:\\/\\/klozzo.com\\/producto-b\", \"referrer\": \"https:\\/\\/example.com\\/blog\\/precios\", \"utm\": { \"utm_source\": \"adwords\", \"utm_medium\": \"ppc\", \"utm_campaign\": \"verano\", \"utm_term\": \"y\", \"utm_content\": \"k\", \"gclid\": \"Cj0KCQjw\", \"fbclid\": \"m\", \"msclkid\": \"y\" }, \"custom_field_values\": [] }" ``` ### Response `200` — Returning person, enriched ```json { "data": { "contact": { "id": "01KZ2P...", "first_name": "Jonathan" }, "contact_created": false, "touch_id": "01KZ2Q...", "applied": { "last_name": "Zapata" }, "skipped": { "source": "immutable" }, "unmapped_keys": [ "presupuesto_mensual" ] } } ``` ### Response `422` — No way to reach them ```json { "message": "Hace falta al menos un correo electrónico o un teléfono para poder identificar a la persona.", "errors": { "email": [ "Hace falta al menos un correo electrónico o un teléfono para poder identificar a la persona." ] } } ```