# Klozzo API > External integrator API for Klozzo. JSON over HTTPS, Bearer token auth. Welcome to the **Klozzo** REST API. - **Base URL**: `https://klozzo.com/api` — the same host shown in every example on this page. - **Auth**: Sanctum personal-access tokens via `Authorization: Bearer `. - **Content**: `application/json`. Errors follow Laravel's `{ "message": ..., "errors": { ... } }` shape. - **Phone numbers**: send them in E.164 — `+525555555555`. See below. - **Rate limiting**: `throttle:api` — **600 requests per minute per API token**. Every response carries `X-RateLimit-Limit` and `X-RateLimit-Remaining`; going over returns `429` with `Retry-After`. Server-event ingestion (`POST /api/v1/events`) has its own budget: `throttle:tracking`, 600 requests per minute per site key. - **Localization**: validation error messages are localized to Spanish by default. Pass `Accept-Language: en` to receive English where available. ### Leads and contacts These two words are not synonyms here, and reading them as if they were is what makes the rest of this page confusing. **A lead is not a separate record.** In Klozzo it is a *contact* plus an *interaction* on top of it — internally a **touch**. There is no leads table to insert into and no lead id to keep. So capturing the same lead ten times gives you **one contact and ten touches**, not ten contacts. "How many leads do I have?" and "how many contacts do I have?" are different questions with different answers, and both are correct. Two things follow, and they are the two things integrators get wrong: - `POST /api/leads` can promise **never** to reject somebody who comes back, because coming back is not a duplicate — it is another touch. It returns both halves: the `contact` it resolved and the `touch_id` it just recorded. - `POST /api/contacts` is a plain insert. It has no touch to add, so a person it has seen before is a collision, and a collision is a `422`. See **[Which endpoint do I use?](https://klozzo.com/docs/guides/which-endpoint)** before you write the first call. ### Phone numbers A number with no country code cannot be called or messaged, and the failure does not show up when you save it — it shows up weeks later, when the WhatsApp never arrives. So the API refuses to guess. Give it the country in one of two ways: - **E.164, country code included** — `"phone": "+525555555555"`. Preferred, and the only form that means the same thing everywhere. - **A local number plus its country** — `"phone": "5555555555"` together with `"phone_country": "MX"` (ISO-3166-1 alpha-2). Anything else is a `422`: ```json { "message": "Falta la lada del país...", "errors": { "phone": ["Falta la lada del país. Manda el número como «+525555555555», o añade «phone_country» (por ejemplo «MX»). Sin lada no podemos enviarle WhatsApp ni llamarle."] } } ``` Notes: - Spaces, dashes and parentheses are fine — `+52 55 5555 5555` parses. - If the number already carries a `+`, that wins and `phone_country` is ignored: a `+57` written on purpose is not a mistyped Mexican number. - Whatever you send is stored normalised to E.164, and that normalised form is what duplicate detection matches on. So `+52 55 5555 5555` and `5555555555` with `phone_country=MX` are the same person, not two. This applies to contacts, leads and bookings. Company phones (`/api/v1/companies`) are free text and are neither validated nor normalised. See **Authenticating requests** below and the **Webhooks & Tracking** appendix at the bottom of this page. > As you scroll, you'll see code examples on the right (or below the text on a narrow screen). Switch languages with the tabs above them.