API de Integração¶
Referência de endpoints para integração entre sistema parceiro e TOMODAT3.
Convenções de autenticação¶
Endpoints de parceiro (server-to-server)¶
Use:
- Header
X-Integration-API-Key: <api_key> - Header
X-Tenant: <tenant_id>quando o endpoint operar no banco do tenant
Endpoints do cliente final dentro do TOMODAT¶
Use JWT de usuário logado (na interface do TOMODAT), não API key do parceiro.
O que é tenant na API¶
tenant é o identificador da conta/ambiente do cliente final no TOMODAT.
Em outras plataformas, isso pode equivaler a:
- conta
- organização
- workspace
- cliente
Quando o endpoint opera no banco do cliente final, envie X-Tenant: <tenant_id>.
Tipos integráveis atualmente suportados¶
Um tipo só é sincronizável quando tem is_integrable = true (flag global do ItemType) e
está configurado para o tenant no vínculo do parceiro (integrated_types, ver "Endpoints de
vínculo de tenant"). Use esses slugs em item_type_slug:
Vertical Hídrica¶
| Nome do tipo | Slug técnico (item_type_slug) |
Geometria esperada (geometry.type) |
Integrável |
|---|---|---|---|
| Cliente Hídrico | cliente-hidrico |
Point |
Sim |
| Reservatório | reservatorio |
Point |
Sim |
| Poço | poco |
Point |
Sim |
Os demais tipos hídricos (
tubulacao,hidrometro) e todos os tipos FTTH não são integráveis por padrão. A integrabilidade é curada por seeder — verdatabase/seeders/Tenant/*/ItemTypesSeeder.php.
Regras importantes:
- O
slugdeve ser enviado exatamente como documentado. geometry.typedeve respeitar a geometria esperada do tipo.- Se o item for criado sem geometria, o usuário final poderá posicioná-lo depois no mapa.
Regra de manutenção desta tabela¶
Sempre que a lista de tipos integráveis mudar no produto, esta documentação deve ser atualizada no mesmo ciclo de entrega.
Sobre properties:
propertiesé JSON livre.- Atualmente não existe schema obrigatório por tipo nessa integração.
- Recomendação: manter estrutura estável no sistema parceiro para facilitar suporte.
Endpoints de itens via integração¶
POST /api/integration/items¶
Cria item integrado.
Headers:
X-Integration-API-KeyX-Tenant
Body exemplo:
{
"external_id": "cli-123",
"item_type_slug": "cliente-hidrico",
"item_model": "casa",
"name": "Joao da Silva",
"geometry": {
"type": "Point",
"coordinates": [-49.1234, -26.4567]
},
"properties": {
"status": "ativo"
},
"external_data": {
"telefone": "47999990000",
"plano": "Residencial"
},
"external_actions": [
{
"label": "Ver cliente no ERP",
"url": "https://erp.exemplo.com/clientes/cli-123",
"icon": "eye"
}
]
}
Resposta sucesso: 201.
Campo item_model (opcional):
- Identifica o modelo do item, casado contra
item_models.code, escopado ao tipo do item. - Define o ícone exibido no mapa (
COALESCE(item_models.icon, item_types.map_icon)) — útil para distinguir subcategorias dentro de um tipo (ex.: "Casa" vs. "Produtor" sobcliente-hidrico). - Envie o code do modelo (ex.:
"casa"), não o nome de exibição. Ver ADR-0013. - Omitido ou nulo → item fica sem modelo. Code inexistente sob o tipo →
422.
Erros comuns:
401: API key ausente/inválida.403: tenant não pertence ao parceiro.404: tipo integrável não encontrado.409:external_idjá existe para esse parceiro no tenant.422: validação de payload, coordenadas não-numéricas, ouitem_model(code) não encontrado sob o tipo.
PUT /api/integration/items/{external_id}¶
Atualiza item integrado por external_id.
Body permitido:
nameitem_model(code do modelo; ver POST)propertiesexternal_dataexternal_actionsgeometry
Regra de item_model (ver ADR-0013): só altera o modelo quando um code não-vazio é enviado;
item_model nulo/omitido preserva o modelo existente (nunca zera via integração).
Regra de geometria (ver ADR-0014):
geometryé aceita, mas aplicada apenas quando o item ainda não tem geometria.- Se o item já tem geometria (posicionamento manual), a coordenada enviada é ignorada silenciosamente — sem erro. Os demais campos são atualizados normalmente.
- Geometria existente nunca é sobrescrita pela integração.
Erros comuns:
404: item não encontrado.422: payload inválido,geometry.typeincompatível com o tipo, coordenadas não-numéricas, ouitem_modelinexistente.
DELETE /api/integration/items/{external_id}¶
Soft delete (item fica inativo).
Resposta sucesso: 200.
Resposta exemplo:
{
"message": "Item desativado",
"deleted_at": "2026-03-12T10:00:00+00:00"
}
POST /api/integration/items/{external_id}/restore¶
Restaura item soft-deleted.
Resposta sucesso: 200.
POST /api/integration/items/sync¶
Sincronização em lote por tipo (full sync).
Body exemplo:
{
"item_type_slug": "cliente-hidrico",
"items": [
{
"external_id": "cli-123",
"name": "Joao da Silva",
"item_model": "casa",
"properties": { "status": "ativo" },
"external_data": { "telefone": "47999990000" },
"external_actions": [
{ "label": "Ver cliente", "url": "https://erp.exemplo.com/clientes/cli-123" }
]
},
{
"external_id": "cli-456",
"name": "Maria Souza"
}
]
}
Comportamento:
- Cria itens novos.
- Atualiza itens existentes (inclui mudança de
item_modelquando umcodenão-vazio é enviado;item_modelnulo/omitido preserva o modelo existente — ver ADR-0013). - Restaura itens soft-deleted presentes no payload.
- Não desativa itens ausentes do payload. A desativação é explícita, via
DELETE /items/{external_id}(ver ADR-0015). Um lote parcial nunca apaga itens. - Geometria: aplicada apenas a itens sem geometria; itens já posicionados têm a coordenada enviada ignorada silenciosamente (nunca sobrescreve — ver ADR-0014).
- Item inválido (
geometry.typeincompatível, coordenadas não-numéricas,item_modelinexistente) é pulado e contado emskipped, sem derrubar o lote. Detalhes ficam emintegration_item_sync_logs.meta.
Cada item do items[] aceita os mesmos campos do POST (item_model, geometry, properties,
external_data, external_actions).
Resposta sucesso: 200.
Resposta exemplo:
{
"created": 5,
"updated": 12,
"restored": 1,
"deactivated": 0,
"unchanged": 81,
"skipped": 1,
"total_received": 100,
"total_in_tomodat": 99
}
deactivated é sempre 0 (mantido por compatibilidade; o sync não desativa — ver ADR-0015).
Endpoints de vínculo de tenant¶
Fluxo A - cliente final gera código, parceiro vincula¶
Passo 1 (cliente final, dentro do TOMODAT):
POST /api/integration/tenant-link-codes(JWT do usuário admin)
Passo 2 (sistema parceiro):
POST /api/integration/tenants/linkcomX-Integration-API-Key
Body exemplo:
{
"code": "A1B2C3D4",
"external_id": "erp_client_42",
"integrated_types": ["cliente-hidrico", "reservatorio", "poco"]
}
Resposta sucesso:
{
"tenant_id": "tenant_abc",
"external_id": "erp_client_42",
"linked": true,
"integrated_types_configured": [
{
"slug": "cliente-hidrico",
"external_base_url": null
},
{
"slug": "reservatorio",
"external_base_url": null
},
{
"slug": "poco",
"external_base_url": null
}
]
}
Erros comuns:
422: código inválido, expirado, já usado, ou tenant vinculado a outro parceiro.401: API key ausente/inválida.
Fluxo B - tenant já é do parceiro, só configurar tipos¶
Endpoint:
POST /api/integration/tenants/{tenant_id}/configure-types
Body exemplo:
{
"integrated_types": [
{
"slug": "cliente-hidrico",
"external_base_url": "https://erp.exemplo.com/api/clientes"
},
{
"slug": "poco",
"external_base_url": "https://erp.exemplo.com/api/pocos"
}
]
}
Dados externos e ações¶
external_data¶
Objeto JSON livre com dados que o TOMODAT exibe para o usuário final.
Exemplo:
{
"telefone": "47999990000",
"status_financeiro": "em_dia",
"plano": "Residencial"
}
external_actions¶
Lista de ações clicáveis para abrir páginas do sistema parceiro.
Formato recomendado:
[
{ "label": "Ver Detalhes", "url": "https://...", "icon": "eye" },
{ "label": "Nova OS", "url": "https://...", "icon": "plus" }
]
Observações:
iconé opcional.urldeve ser absoluta.- Links são abertos em nova aba no frontend.
Sobre endpoint de detalhes externos (proxy)¶
Na versão atual, o fluxo oficial usa dados cacheados (external_data e external_actions).
Um endpoint proxy para buscar detalhes online no sistema parceiro pode ser adicionado em versão futura, mas não faz parte do contrato obrigatório atual.
Export KML¶
GET /api/export/kml¶
Exporta todos os itens com geometria do tenant atual como arquivo KML (Google Earth).
Autenticação: JWT do usuário logado do tenant (não usa API key de parceiro). Acessível também pelo iframe embed.
Headers:
Authorization: Bearer <jwt>X-Tenant: <tenant_id>
Comportamento:
- Itens são agrupados por tipo em
<Folder>. - Um
<Style>por modelo (ou por tipo, quando o item não tem modelo) carrega o ícone do mapa; linhas usamline_color/line_widthdas specs do modelo, quando definidos. - Coordenadas em ordem KML
lon,lat(WGS84). - Itens inativos (soft-deleted) são omitidos.
Resposta: 200, Content-Type: application/vnd.google-earth.kml+xml, com
Content-Disposition: attachment; filename="tomodat-export.kml".
Fluxo do entregável IAT: exportar o KML → abrir no Google Earth Pro → montar o PDF a partir do mapa.
Embed token e iframe¶
Endpoint para gerar token:
POST /api/integration/embed-token
Body:
{
"tenant_id": "tenant_abc"
}
Resposta:
{
"embed_token": "<jwt>",
"expires_in": 3600
}
Para renovação automática via postMessage, veja Renovação de Embed Token.
Troubleshooting rápido¶
401 Unauthorized¶
- API key ausente ou inválida.
- Token JWT expirado no fluxo de iframe.
403 Forbidden¶
- Tenant não pertence ao parceiro da API key usada.
422 Unprocessable Entity¶
- Payload inválido.
- Tipo integrável inexistente.
- Código de vínculo expirado/já usado.
429 Too Many Requests¶
- Rate limit em geração de código de vínculo ou endpoints públicos.