Skip to content
Klozzo API
OpenAPI Postman

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 <token>.
  • Content: application/json. Errors follow Laravel's { "message": ..., "errors": { ... } } shape.
  • Phone numbers: send them in E.164 — +525555555555. See below.
  • Rate limiting: throttle:api600 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? 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:

{
  "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.