Menuabrir
EstávelAtualizado em 14 de mai. de 2026, 00:06

Este módulo depende de

4
  • agentsHub.7 DMs inbound → agente; Hub.8 comentários → agente
  • crmResolve/cria contato de DM inbound
  • businessAgente consulta brand_voice, products, forbidden_topics via tools brain
  • settingsConsulta ai_auto_reply_enabled (kill switch global)

Módulos que dependem deste

2
  • connectionsAgrega status de contas Instagram conectadas
  • agentsHub.7 DM inbound; Hub.8 comment inbound → agente

Instagram

Status: 🟢 Estável (Hub.5 + Hub.7 + Hub.7.1 + Hub.8 deployados) Code: backend/app/modules/instagram UI: frontend/src/app/(admin)/[slug]/admin/instagram Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: agents (Hub.7 DM + Hub.8 comments), crm (resolução de contato), settings (kill switch global), tenant_router


1. Identidade

O que faz (uma frase)

Conecta conta Instagram Business via Graph API, persiste posts/comentários/DMs, roteia DMs (Hub.7) e comments públicos (Hub.8) pro Agente IA, e expõe insights orgânicos (reach, saves, shares) pro Conselho — tudo com token cifrado por tenant.

Por que existe (negócio)

Instagram é segundo canal de aquisição do Mais Consciente (depois de WhatsApp). Sem este módulo:

  • DMs ficam no app móvel sem histórico no CRM.
  • Comentários públicos não recebem resposta consistente.
  • Insights orgânicos (alcance, engajamento) ficam só no Insights Meta — sem cruzar com revenue.

Hub.5 trouxe observabilidade orgânica, Hub.7 automação de DMs (Agente responde 24/7), Hub.8 automação de comments (resposta pública prudente, sem vazar dado privado).

Por que existe (técnico)

Como módulo dedicado:

  • Adapter Graph API isolado (mudanças Meta v22 → metric_type = time_series vs total_value).
  • Token cifrado (Hub.7.1) — envelope AES-256-GCM por tenant, KEK derivada via HMAC.
  • Brain.respond_comment ≠ respond_dm — comment é público, sem tools, prompt seguro.
  • Idempotência via external_id UNIQUE em instagram_message e instagram_comment.
  • Webhook signature HMAC-SHA256 timing-safe + verify_token.

Status atual

  • 2026-05-13 — Hub.5/7/7.1/8 todos deployados (commits 2f12c65, 4db4beb, 9f56985, d6af3dd; alembics 0106-0110).
  • 3 contas backfilladas com token cifrado.

Próxima mudança: Hub.7.2 — drop coluna legacy instagram_account.access_token_encrypted (~2 semanas após validação).


2. Cases de uso reais

Case 1: Lead manda DM no @maisconsciente

Fluxo: Webhook IG field=messaging_upsert_dm (idempotente via external_id) → enqueue dispatch_to_agent_taskorchestration.resolve_agent_for_instancebrain.respond_dm(channel=instagram) → Graph API /messages com messaging_type=MESSAGE_TAG&tag=HUMAN_AGENT → persist outbound.

Impacto: Resposta em <30s sem Felipe estar online.

Case 2: Comment público "qual o preço?" em post

Fluxo: Webhook field=commentspersist_inbound_comment (idempotente via external_id unique) → enqueue dispatch_comment_to_agent_taskbrain.respond_comment (SEM tools, max 400 tokens, prompt segurança) → Graph API /{comment_id}/replies → persist outbound + status=replied.

Impacto: Comments respondidos sem risco de vazamento de dado privado.

Case 3: Conselheiro Content & Brand consulta insights

Fluxo: Conselheiro chama tool read_instagram_* → cross_module retorna reach, saves, shares, total_interactions, engagement_rate por post (fallback progressivo de métricas por metric_type IG v22).

Impacto: Análise de performance de conteúdo sem abrir o painel Meta.


3. Oportunidades de negócio

  • Reaction-based marketing: detectar reactions (emojis em DMs) → trigger campanhas baseadas em humor/intent.
  • Auto-resposta segmentada por persona: posts diferentes têm públicos diferentes — agente identifica e ajusta tom.
  • Insights cross-tenant anonimizados: quais formatos performam melhor por vertical (Reels vs Carrossel) → produto premium.
  • Story-to-DM conversion tracking: medir conversão de story para conversa real (Hub.5+).
  • Comments moderation as a service: Hub.8 + ML detection de spam/ódio → produto pra empresas grandes.

Riscos: Meta muda API frequentemente (v22 → v23 vai mudar metric_type novamente); Brand Account quirks; 24h window para DMs sem HUMAN_AGENT tag.


4. Arquitetura interna

Arquivos

ArquivoPropósito
models.py4 tabelas
routes.py19 endpoints + 2 webhook
service.pyGraph API adapter + crypto
tasks.pydispatch_to_agent_task (Hub.7), dispatch_comment_to_agent_task (Hub.8)
schemas.pyPydantic

Tasks Celery

TaskTriggerIdempotênciaRetry
instagram.dispatch_to_agentWebhook DM inbound persiste → enqueue se newStatus check em instagram_message; pause checks (account + thread)max 2, countdown 30s, só transient (no_db, timeout, connection)
instagram.dispatch_comment_to_agentWebhook comment persiste → enqueue se newStatus check em instagram_comment (status=received); idempotente via ON CONFLICT DO NOTHING no webhookmax 2, transient only

Ambas usam worker_session (NullPool) — fix loop-mismatch.

Adapter Graph API

  • Endpoint base: https://graph.instagram.com/v21.0/{ig_user_id}/...
  • Auth: Bearer access_token em query params.
  • Webhook: POST /api/instagram/webhook. Verifica X-Hub-Signature-256 (HMAC-SHA256 timing-safe). Verify token via INSTAGRAM_WEBHOOK_VERIFY_TOKEN.

Token cifrado (Hub.7.1)

  • Coluna nova: instagram_account.access_token_envelope (JSONB) — envelope AES-256-GCM via whatsapp_crypto.encrypt_for_tenant(token, tenant_id).
  • Coluna legacy: instagram_account.access_token_encrypted (TEXT) — débito Hub.7.2 (drop após validação).
  • KEK por tenant: HMAC-SHA256(MASTER_KEY, tenant_id.bytes) — determinística.
  • Decrypt: prioriza envelope; fallback pra legacy com log warn.

5. Tabelas + relacionamentos

instagram_account

Coluna chaveNotas
tenant_id (idx)
ig_user_idIG numeric ID (não username)
username / name / followers_countSnapshot perfil
fb_page_idLinked Facebook page
access_token_envelope (JSONB)Hub.7.1 envelope cifrado
access_token_encrypted (TEXT)Legacy (Hub.7.2 dropar)
token_expires_atLong-lived token vencimento
is_activeSoft-delete
agent_paused_atPausa global IA pra DMs + comments
connected_at / disconnected_at / last_synced_atTimestamps

instagram_thread

Coluna chaveNotas
tenant_id
external_id (unique)Chave simetrizada <ig_id_a>_<ig_id_b> (ordenados)
contact_ig_id / contact_username / contact_nameQuem iniciou DM
statusopen / resolved / paused
unread_count / last_message_atUI ordering
contact_idFK lógica → crm_contact
agent_paused_atPausa local (humano assumiu)

instagram_message

Coluna chaveNotas
thread_id (FK CASCADE)
external_id (UNIQUE COALESCE)Meta message ID — dedup via ON CONFLICT DO NOTHING
directioninbound / outbound
kindtext / image / video / etc.
content / media_url / media_mime
statussent / delivered / failed
agent_idFK lógica → agents_agent quando enviado por agente

instagram_comment

Coluna chaveNotas
tenant_id (idx)
ig_user_id (idx)Side do post (nosso perfil)
media_id (idx)Post/Reel ID
external_id (UNIQUE)Crítico — dedup
parent_external_idReply-to comment (thread de comentários)
directioninbound / outbound
author_*Quem comentou
textConteúdo
like_countSnapshot
hiddenModeração manual
statusreceived / replied / hidden / ignored / deleted / error
agent_idQuem respondeu
contact_idFK lógica → CRM

Relacionamentos cross-módulo

DireçãoOutro móduloComo
↘ Escrevecrmresolve_or_create_contact_from_ig no dispatch
↘ Escreveagentsdispatch_to_agent_task cria agents_run
↗ Lêbusinessread_brain_config, read_products (via brain tools)
↗ Lêsettings_identityai_auto_reply_enabled (kill switch global)
↗ Lêagents_instance_modeModo (orchestrator/dedicated/team) — Manager + pool

6. API / Endpoints (~19 + 2 webhook)

Prefixo /api/instagram.

Conta (5)

MétodoRotaO que faz
GET/accountInfo da conta conectada
GET/auth/url?redirect_uri=...OAuth URL
POST/auth/callbackTroca code por long-lived token
POST/account/setupSetup manual com token (Graph Explorer)
POST/account/disconnectSoft delete

Analytics / Posts (2)

MétodoRotaO que faz
GET/posts?limit=30&since=...&until=...Posts com métricas (fallback progressivo IG v22)
GET/audienceDemographics, reach 28d, profile views

Comentários (4)

MétodoRotaO que faz
GET/comments?limit=50Comentários recentes
POST/comments/{id}/replyResponde (público)
POST/comments/{id}/hideOculta
DELETE/comments/{id}Deleta

DMs (4)

MétodoRotaO que faz
GET/dms?status=open|all&q=&limit&offsetLista threads
GET/dms/{thread_id}Thread + mensagens (zera unread)
POST/dms/{thread_id}/agent-pausePausa IA local
POST/dms/{thread_id}/agent-resumeRetoma

Configuração de Agentes (3)

MétodoRotaO que faz
POST/account/agent-pausePausa IA conta toda
POST/account/agent-resumeRetoma
GET / PUT/agents-configModo (orchestrator/dedicated/team) + pool

Webhook (2)

MétodoRotaO que faz
GET/webhookVerify Meta (hub.challenge)
POST/webhookReceive (HMAC-SHA256)

7. Configuração

Env vars

VarPropósito
INSTAGRAM_APP_IDOAuth
INSTAGRAM_APP_SECRETOAuth + HMAC
INSTAGRAM_WEBHOOK_VERIFY_TOKENVerify token
MANDIR_CRYPTO_MASTER_KEYHub.7.1 envelope

Kill switches em cascata

  1. Global: settings_identity.ai_auto_reply_enabled = false.
  2. Por conta: instagram_account.agent_paused_at NOT NULL.
  3. Por thread: instagram_thread.agent_paused_at NOT NULL.
  4. Por status: comment status≠received ou direction≠inbound → skip.

8. Operações

Como conectar conta nova

  1. UI /admin/instagram/settings → OAuth URL.
  2. User autoriza no Instagram (Brand Account exige Workspace owner direto, não Manager pessoal — memória [[oauth-brand-account-youtube]] aplica também).
  3. Callback troca code por long-lived token (60d).
  4. Token cifrado em envelope + persistido em instagram_account.access_token_envelope.

Troubleshooting

Sintoma: Webhook chega mas DM não responde

Causas: kill switch global, account pause, thread pause, kill switch agent, agente inativo, loop-mismatch. Diagnóstico:

SELECT id, agent_paused_at FROM instagram_account WHERE tenant_id=...;
SELECT id, status, agent_paused_at FROM instagram_thread WHERE external_id=...;

Logs: docker logs mandir-suite-worker | grep instagram.dispatch.

Sintoma: Comment respondido vira erro

Causa: Token expirado, post deletado, ou rate limit Meta. Fix: Reconectar conta (refresh token); verificar instagram_comment.status=error.


9. Métricas e observabilidade

Logger keyQuando emite
instagram.dm.receivedWebhook DM persistiu
instagram.dm.dispatch.startTask enfileirada
instagram.dm.dispatch.completedBrain respondeu + outbound enviado
instagram.comment.receivedWebhook comment persistiu
instagram.comment.dispatch.repliedReply pública enviada
instagram.comment.dispatch.skippedBrain decidiu não responder (privacy)
instagram.token.refreshLong-lived renovado

10. Limitações e débitos técnicos conhecidos

#ItemImpactoPlano
1Coluna legacy access_token_encryptedLow — débito Hub.7.2Drop após ~2 semanas validação
224h window pra DMsMid — fora janela exige HUMAN_AGENT tagOK (já implementado defensivamente)
3Brand Account OAuth quirkMid — exige Workspace owner diretoDoc de onboarding
4Metric_type IG v22Mid — time_series vs total_valueFallback progressivo já implementado
5Insights não persistidos em DBMid — retorna na response, sem históricoTabela instagram_*_insight futura
6Sem story trackingMid — Hub.5 não cobre storiesHub.5+
7Webhook único por App MetaLow — 1 webhook URL pra todos os tenantsResolve via ig_user_id no payload

11. Histórico relevante

  • 2026-05-13 (commit d6af3dd, mig 0110_ig_comments) — Hub.8: comments → agente.
  • 2026-05-13 (commit 9f56985, mig 0109_ig_token_envelope) — Hub.7.1: token cifrado.
  • 2026-05-13 (commit 4db4beb, mig 0108) — Hub.7: DM → agente.
  • 2026-05-13 (commit 2f12c65, mig 0106) — Hub.5: 6 tabelas ig_* + 6 tools Council. Memória [[sessao-2026-05-13-hub56]].
  • 2026-05-13 (commit e1823b7) — Fix metric_type correto (time_series vs total_value) IG v22.