# Agendo — Agent API & MCP > API HTTP + servidor MCP para uma IA ou dev consultar times, funis, leads e pixels do Agendo (leitura) e criar/editar rascunhos de funil (escrita F1a) com um token escopado (PAT). PII de lead vem mascarada e o token de pixel (Meta CAPI) nunca é exposto. Escrita é dry-run-first + confirmação. ## Links - Documentação: https://docs.useagendo.com - Agent API (base): https://api.useagendo.com/agent/v1/{tool} - MCP (Streamable HTTP, JSON-RPC via POST): https://mcp.useagendo.com/mcp - App: https://app.useagendo.com ## Autenticação Todas as chamadas usam um Personal Access Token (PAT) escopado no header: Authorization: Bearer cal_agent_... O token cru aparece uma única vez na geração; o servidor guarda só o hash. Sem token / inválido / expirado / revogado => 401 unauthorized. ## MCP Endpoint: https://mcp.useagendo.com/mcp (Streamable HTTP stateless; envie JSON-RPC via POST; GET responde 405 por design). Conectar (genérico, clientes que leem config MCP): { "mcpServers": { "agendo": { "url": "https://mcp.useagendo.com/mcp", "headers": { "Authorization": "Bearer cal_agent_..." } } } } As 10 tools MCP (8 de leitura + 2 de escrita, snake_case) mapeiam 1:1 para as tools da Agent API (camelCase). Mesma autenticação, mesma máscara de PII. ## Agent API Cada tool é POST em https://api.useagendo.com/agent/v1/{tool} com corpo JSON. Resposta OK: { "ok": true, "tool": "...", "mode": "read" | "dryRun" | "exec", "data": { ... } } Resposta erro: { "ok": false, "tool": "...", "error": { "code": "...", "message": "...", "retryable": false } } ## Tools de leitura (8) - get_me (Agent API: getMe) — scopes: agent:read — Identidade do dono do PAT: usuário, scopes e times permitidos. input: Sem parâmetros. output: userId, name, email, username, locale, timeZone, allowedTeamIds, scopes exemplo: POST https://api.useagendo.com/agent/v1/getMe body {} - list_teams (Agent API: listTeams) — scopes: agent:read — Times visíveis pelo PAT (restritos ao allowedTeamIds). input: Sem parâmetros. output: teams[]: teamId, name, slug, role exemplo: POST https://api.useagendo.com/agent/v1/listTeams body {} - list_funnels (Agent API: listFunnels) — scopes: agent:read, funnels:read — Lista funis do escopo. Sem teamId (ou null) = funis pessoais. input: teamId? (número | null) output: funnels[]: id, uid, name, teamId, enabled, pixelEnabled, leadsEnabled, stepCount exemplo: POST https://api.useagendo.com/agent/v1/listFunnels body { "teamId": 12 } - get_funnel (Agent API: getFunnel) — scopes: agent:read, funnels:read — Detalhe de 1 funil + resumo das etapas. Informe funnelId OU funnelUid. input: funnelId? (número) | funnelUid? (texto) — um obrigatório output: id, uid, name, slug, teamId, enabled, pixelEnabled, leadsEnabled, hasCustomDomain, headline, subhead, eventTypeId, stepCount, hasQualification, hasSchedule, closingKind, steps exemplo: POST https://api.useagendo.com/agent/v1/getFunnel body { "funnelId": 8 } - list_pixels (Agent API: listPixels) — scopes: agent:read, pixels:read — Pixels Meta (CAPI) do escopo. Só hasToken — o token de acesso nunca sai. input: teamId? (número | null) output: pixels[]: id, teamId, name, pixelId, enabled, sendScope, statusEventMap, leadEventSource, lastTestStatus, hasToken exemplo: POST https://api.useagendo.com/agent/v1/listPixels body { "teamId": 12 } - get_pixel_health (Agent API: getPixelHealth) — scopes: agent:read, pixels:read — Saúde de 1 pixel (último teste local). A entrega real é o Events Manager da Meta. input: pixelId (número, obrigatório) output: id, teamId, name, pixelId, enabled, sendScope, hasToken, lastTestedAt, lastTestStatus, note exemplo: POST https://api.useagendo.com/agent/v1/getPixelHealth body { "pixelId": 5 } - list_leads (Agent API: listLeads) — scopes: agent:read, leads:read — Lista leads do escopo. PII vem mascarada. Filtros opcionais e paginação. input: teamId? · search? · status? · source? · limit (1–100, padrão 50) · offset (padrão 0) output: total, limit, offset, items[]: id, uid, name, emailMasked, phoneMasked, status, source, value, teamId, createdAt exemplo: POST https://api.useagendo.com/agent/v1/listLeads body { "teamId": 12, "limit": 20 } - get_lead (Agent API: getLead) — scopes: agent:read, leads:read — Detalhe de 1 lead (PII mascarada). Informe leadId OU leadUid. input: leadId? (número) | leadUid? (texto) — um obrigatório output: id, uid, name, emailMasked, phoneMasked, cpfMasked, status, source, value, teamId, qualification, createdAt exemplo: POST https://api.useagendo.com/agent/v1/getLead body { "leadId": 4567 } ## Escrita (F1a) — funis AVISO: Escrita por MCP existe HOJE só para FUNIS (create_funnel_draft, update_funnel_draft — scope funnels:write). Leads, Webhook e Pixel continuam SOMENTE LEITURA — não há leads:write / webhooks:write / pixels:write por MCP ainda. Tools de escrita (scope funnels:write): - create_funnel_draft (Agent API: createFunnelDraft) — scopes: funnels:write — Cria um rascunho de funil/formulário. Exige funnels:write. SEMPRE chame com dryRun=true primeiro. input: name (obrigatório) · teamId? (null = pessoal) · slug? · enabled? · headline? · subhead? · steps? · pixelEnabled? · leadsEnabled? · dryRun (padrão true) · confirmationToken (no exec) output: dryRun → { dryRun:true, preview:{action,targetId,wouldChange}, confirmationToken, expiresAt } · exec → { dryRun:false, action, resource:{id,uid,name,slug,enabled,teamId} } exemplo (dry-run): POST https://api.useagendo.com/agent/v1/createFunnelDraft body { "name": "Funil Black Friday", "dryRun": true } - update_funnel_draft (Agent API: updateFunnelDraft) — scopes: funnels:write — Edita um funil/formulário existente (informe id). Exige funnels:write. SEMPRE dryRun=true primeiro. input: id (obrigatório) · name (obrigatório) · teamId? · slug? · enabled? · steps? · pixelEnabled? · leadsEnabled? · dryRun (padrão true) · confirmationToken (no exec) output: igual a create_funnel_draft (action = "updated"). exemplo (dry-run): POST https://api.useagendo.com/agent/v1/updateFunnelDraft body { "id": 8, "name": "Funil Black Friday 2", "enabled": false, "dryRun": true } Fluxo OBRIGATÓRIO (dry-run-first + confirmação): - 1. dryRun=true: Valida o payload + permissões + cobrança e monta um PREVIEW do que mudaria. NÃO grava nada. Retorna um confirmationToken. - 2. Revisar o preview: Confira o que SERIA criado/alterado antes de executar. - 3. dryRun=false + confirmationToken: Reenvie o MESMO payload com o confirmationToken do passo 1. Só aí grava. Payload diferente → recusado. Detalhes: - dryRun=true NÃO grava; retorna preview + confirmationToken (válido ~5 min, preso ao token+tool+payload). - dryRun=false exige o confirmationToken; o payload deve ser IDÊNTICO ao do dry-run (senão confirmation_invalid). - confirmationToken é de uso ÚNICO (reenviar => confirmation_invalid; anti-replay). - Header opcional Idempotency-Key: mesma key+payload => resposta cacheada; key+payload diferente => idempotency_conflict. - Token sem funnels:write => forbidden_scope. ## Scopes - agent:read: Base — exigido por toda tool de leitura (get_me, list_teams). - funnels:read: list_funnels, get_funnel. - pixels:read: list_pixels, get_pixel_health. - leads:read: list_leads, get_lead. - funnels:write: ESCRITA (F1a) — create_funnel_draft, update_funnel_draft. Exige dryRun + confirmationToken. Além do scope, o PAT só enxerga os times do seu allowedTeamIds (2ª camada de acesso). ## Segurança - PII mascarada: leads voltam emailMasked/phoneMasked/cpfMasked; nunca o valor cru. - Token CAPI oculto: pixels expõem só hasToken (bool); o access token da Meta nunca sai. - Auditoria: cada chamada registra um AgentRequest (tool, status; sem corpo/PII). - Rate-limit: por PAT (429 rate_limited, com header Retry-After). - Escrita com guarda: só funis (funnels:write); dry-run obrigatório + confirmationToken (payload-locked, uso único) + idempotência opcional; cada chamada (dryRun e exec) auditada sem payload cru/PII. Leads, Webhook e Pixel seguem somente leitura. ## Troubleshooting / Erros - unauthorized (HTTP 401): PAT ausente, inválido, expirado ou revogado. - forbidden_scope (HTTP 403): Falta um scope exigido pela tool (ex.: write sem funnels:write). - forbidden_team (HTTP 403): teamId fora do allowedTeamIds do PAT. - not_found (HTTP 404): Recurso inexistente OU sem acesso (anti-enumeração). - validation_error (HTTP 400): Input inválido (ex.: get_funnel sem funnelId/funnelUid). - rate_limited (HTTP 429): Acima do limite por PAT. Tem header Retry-After. - internal (HTTP 500): Erro inesperado (mensagem genérica, sem stack). - confirmation_required (HTTP 400): Escrita com dryRun=false SEM confirmationToken (rode dryRun=true antes). - confirmation_invalid (HTTP 403): confirmationToken inválido, payload trocado, ou já usado (replay bloqueado). - confirmation_expired (HTTP 400): confirmationToken expirou (5 min). Rode dryRun de novo. - idempotency_conflict (HTTP 409): Idempotency-Key reutilizada com payload diferente. - conflict (HTTP 409): Conflito de estado (ex.: slug de funil já existe no escopo). Dicas: - 401 => revise o PAT (ausente, expirado ou revogado) e o header Authorization. - 403 forbidden_scope => gere um PAT com o scope exigido pela tool. - 403 forbidden_team => o teamId está fora do allowedTeamIds do PAT. - GET https://mcp.useagendo.com/mcp => 405: use POST (JSON-RPC); o MCP é stateless.