Handoff Contract.
Handoff Contract v1 is the interface your CRM implements to receive leads from Jataí: three token-authenticated HTTP routes (health, receive lead and check status) plus one callback route, on Jataí’s side, for the outcome. The lead arrives with the organization, the person, the numbers, the reason and the conversation history. It never carries a pipeline stage.
What Entrega (handoff) sends to your CRM, field by field. Versioned: v1 keeps working when v2 comes out.
Overview.
Jataí doesn’t write an adapter for each CRM. It publishes a contract, and each platform implements it in its own repository, running the conformance suite until it’s green. Whoever plugs in a brand doesn’t need anyone from Jataí in the middle — and Jataí doesn’t need to know the CRM.
For a legacy system that won’t implement any route, there’s the generic adapter: it maps the fields through configuration, with no new code, and refuses to write any field that reveals a pipeline stage.
The routes.
| Route | Who implements it | What for |
|---|---|---|
POST {base}/health | your CRM | declares which contract versions it speaks ({ "versoes": ["v1"], "ok": true }). Without this, Jataí would have to guess the format. |
POST {base}/leads | your CRM | receives the handed-off lead. Idempotent by externalId: the same key twice produces only one lead, and a redelivery responds duplicado: true, not an error. |
GET {base}/leads/{externalId} | your CRM | reconciliation, not sync: says whether it knows the lead and what outcome it’s in. An unknown key responds 200 with `conhecido: false`, never 404. |
POST /handoff/retorno | Jataí | the only way an outcome gets into Jataí. Your CRM calls this route, authenticated with the brand’s API key. |
All of them require a token. A route that receives a brand’s real people’s data without a token would be public — and the suite rejects any platform that accepts a handoff without authentication.
The lead: `POST {base}/leads`.
| Field | What it is |
|---|---|
versao | always "v1" |
externalId | the idempotency key (up to 120 characters) |
entregueEm | the moment of handoff, in ISO 8601 |
gatilho | why the lead is being handed off now: resposta_positiva, selecao_em_lote, envio_individual or temperatura |
organizacao | nome, site, tipo, pais, cidade and identificadores (a map: CNPJ, domain…) |
pessoa | nome, cargo and contatos[]: each with tipo (email or whatsapp), valor, confianca (0 to 1) and verificadoEm |
fit | an integer from 0 to 100: how well the organization matches the brand’s profile |
jataiTemperatura | an integer from 0 to 100: the priority signal, for the salesperson. Goes into the jatai_temperatura field |
valorEstimado | number or null |
porQue | the sentence the operator read in the queue: why this lead got here |
angulo | the angle used in the outreach, or null |
historico[] | up to 50 messages: de (nos or eles), canal, assunto, corpo and em |
atributos | a free-form object for whatever the brand wants to carry along |
{
"versao": "v1",
"externalId": "lead-8f3a2c",
"entregueEm": "2026-09-18T09:20:00-03:00",
"gatilho": "resposta_positiva",
"organizacao": {
"nome": "Instituto Horizonte de Formação",
"site": "https://exemplo.org.br",
"tipo": "instituto",
"pais": "BR",
"cidade": "Goiânia",
"identificadores": { "cnpj": "••.•••.•••/••••-••" }
},
"pessoa": {
"nome": "Helena Prado",
"cargo": "Academic coordination",
"contatos": [
{ "tipo": "email", "valor": "helena@exemplo.org.br", "confianca": 0.9, "verificadoEm": null }
]
},
"fit": 84,
"jataiTemperatura": 82,
"valorEstimado": null,
"porQue": "Replied asking for prices; the committee meets on the 22nd.",
"angulo": "New class in February: teaching-material decision by November.",
"historico": [
{ "de": "nos", "canal": "email", "assunto": "February class", "corpo": "…", "em": "2026-09-15T09:00:00-03:00" },
{ "de": "eles", "canal": "email", "assunto": "Re: February class", "corpo": "…", "em": "2026-09-18T09:12:00-03:00" }
],
"atributos": {}
}The field that will never exist.
Pipeline stage. Jataí writes activity and jatai_temperatura, never stage. Two authors on the same field destroy the team’s trust in the CRM, and that doesn’t come back. That’s why the contract doesn’t have the field — it’s not a permanent null, it’s absent —, and the suite rejects any platform that requires a stage to accept a lead.
The callback: `POST /handoff/retorno`.
Jataí doesn’t poll your CRM. Your CRM is the one that reports back, when something happens to a handed-off lead.
| Field | What it is |
|---|---|
versao | always "v1" |
externalId | the key of the handed-off lead |
resultado | virou_cliente, perdido, duplicado or atividade_humana |
em | when it happened, in ISO 8601 |
detalhe | optional free text (up to 1,000 characters) |
`atividade_humana` is the most important callback. After the handoff, Jataí still owns the automated channel and your salesperson owns the direct conversation: an automated message on top of a human conversation burns the lead and the brand. This callback pauses every automated Rota (sequence) for that lead, and it only resumes if the lead is marked as lost or goes cold.
An unknown key responds 200 with conhecido: false: it may be the redelivery of an old callback, and a 4xx would make the platform retry forever.
The conformance suite.
The suite runs against your real implementation, with test data — it writes to the platform, with externalId prefixed by conformance- so you can find and delete it afterwards. It exits with code 1 when something fails, so it can be a CI step. There are eight checks:
- health declares the version — without this, Jataí would have to guess the format.
- accepts a valid lead — this is the route that receives the work of the whole machine.
- the same key twice produces only one lead — Jataí resends every time it doesn’t get the ACK, and it will resend.
- can report the status of a known lead — this is how Jataí reconciles when it suspects it missed a callback.
- an unknown key returns 200 with `conhecido: false` — a 404 is indistinguishable from a route that’s down.
- rejects an invalid payload with a 4xx, instead of silently accepting it — a lead accepted and lost is worse than a lead rejected.
- rejects anyone without the token.
- doesn’t require a pipeline stage.
jatai-conformance --url https://sua-plataforma/jatai --token SEU_TOKEN
# the token can also come from JATAI_TOKENThe suite is the @jatai/conformance package, provided to whoever is going to plug in a platform. Ask for it in your invite.
Versioning.
The versao field says which contract the lead follows, and health says which ones your CRM speaks. Breaking a contract is the most expensive thing there is when many consumers depend on it — that’s why v1 keeps working when v2 comes out.
Frequently asked questions
What happens if my CRM doesn’t respond to the lead?
Jataí resends — that’s why the route is idempotent. The same externalId twice produces only one lead, and a redelivery responds duplicado: true, not an error.
Does Jataí change the lead’s stage in my CRM?
Never. The contract doesn’t have the field. Jataí writes activity and temperature; the stage belongs to your salesperson.
How does Jataí know the lead became a customer?
Your CRM tells it, by calling POST /handoff/retorno with resultado: "virou_cliente". Jataí doesn’t poll the CRM.
What if my system can’t implement the routes?
For a legacy system, the generic adapter maps the fields through configuration — with no new code — and refuses to write any field that reveals a pipeline stage.