modules/sync_ingest — a porta de entrada dos dados
API pelo qual o sincronizador do cliente empurra dados para o espelho. É o modo C do contrato (cliente empurra por API); o modo B (views no banco do ERP, conector puxando) ainda não existe.
Regra de ouro deste módulo
A organização vem sempre do X-Sync-Token, nunca do JWT nem da organização padrão. É a única
família de rotas com essa regra — route_sync_ingest.py abre a sessão do espelho explicitamente
por tenants.engine_for(org_id, "sync"), sem passar pelo get_sync_db. Se a organização não tem
espelho provisionado, responde 503.
O token é comparado por SHA-256 contra ent-sync-token (não revogado) e carimba last_used_at.
Contrato
GET /sync/v1/entities
Devolve, para cada entidade alimentável: o nome, a chave de upsert e as colunas lidas por introspecção do banco real da organização (cache de 300s).
Isso é uma boa decisão: o contrato não é uma lista fixa no código — é o que aquele banco aceita hoje. Se uma migration do template acrescentar coluna, o contrato acompanha sozinho.
POST /sync/v1/{entidade}/batch
{ "records": [ { "app_id": "...", "col": "valor" } ] }- Chave:
app_idpara tudo, excetoembalagem, que usachv_reg. INSERT ... ON CONFLICT (chave) DO UPDATE SETem todas as colunas enviadas.- Máximo 1000 registros por lote (
413acima). - Coluna desconhecida é
422, não descarte silencioso. Deliberado: o cliente descobre o erro de mapeamento na hora, em vez de perder dado em silêncio. - Registro sem a chave:
422.
POST /sync/v1/heartbeat
Devolve {status: ok, org_id}. Não grava nada.
Estado real
O caminho feliz funciona e é o que popula a organização demo. Quatro buracos — detalhados em flx-primeira-carga, resumidos aqui:
| Buraco | Consequência |
|---|---|
3 entidades sem app_id (app_estoque_chpben, app_metadata_dictionary, app_sync_control) são anunciadas e recusadas com 422 | Impossível ingerir. É por isso que app_sync_control está zerada e o heartbeat não deixa rastro. |
| 32 entidades ingeríveis contra 51 tabelas no template | Faturamento, notas fiscais, títulos e dimensões não têm caminho de carga. Esses módulos só funcionam na demo. |
| Sem cursor por entidade e sem reconciliação | Toda carga é cheia; registro apagado no ERP nunca é detectado. |
| Nenhuma tela mostra o estado | /settings/sync/erp é mockup com números fictícios. |
Onde isto aparece para o cliente
Em lugar nenhum, hoje. O TI do cliente precisa: receber o token por fora, descobrir o contrato
chamando GET /entities na unha e mapear as colunas sem documentação por entidade. A tese do
produto — “o TI do cliente integra sozinho” — depende de uma página de Integração que ainda não
existe.
Lacunas
Rastreadas na fase-1-onboarding.