Introdução
A Agendo Agent API é uma API HTTP para uma IA ou dev consultar partes do Agendo (times, funis, leads, pixels) — e agora também criar/editar rascunhos de funil com escrita controlada — usando um token escopado, sem navegador e sem cookie de sessão.
Há dois jeitos de consumir: HTTP direto (POST nas tools da Agent API) ou MCP (servidor remoto que seu agente de IA conecta). Os dois usam o mesmo token e as mesmas tools: 8 de leitura + 2 de escrita (funis — funnels:write, com dry-run + confirmação).
- Agent API:
https://api.useagendo.com/agent/v1/{tool} - MCP:
https://mcp.useagendo.com/mcp - App: https://app.useagendo.com
Autenticação
Toda chamada usa um Personal Access Token (PAT) escopado, no header. O token cru aparece UMA vez (na geração); o servidor guarda só o hash.
Authorization: Bearer cal_agent_...Sem token, ou token inválido/expirado/revogado → 401 unauthorized.
Gerar PAT
O PAT é gerado dentro do app (com escopo e times definidos por você). Ao gerar, você escolhe:
- Scopes — leitura (ex.:
agent:read,leads:read) e/ou escrita de funil (funnels:write, com dry-run + confirmação). - Times (
allowedTeamIds) — quais times o PAT enxerga. - Validade — expira por padrão; pode ser revogado a qualquer momento.
Guarde o token com cuidado — ele dá acesso de leitura ao escopo concedido. Em caso de vazamento, revogue e gere outro.
Conectar MCP
Para usar via agente de IA, adicione o servidor MCP remoto e autentique com o PAT. Passo a passo completo na página de mcp.useagendo.com.
# Claude Code
claude mcp add --transport http agendo \
https://mcp.useagendo.com/mcp \
--header "Authorization: Bearer cal_agent_..."Primeiro teste (get_me)
Confirme que o token funciona pedindo a própria identidade:
curl -s -X POST "https://api.useagendo.com/agent/v1/getMe" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" -d '{}'Resposta de exemplo (valores fictícios):
{
"ok": true,
"tool": "getMe",
"mode": "read",
"data": {
"userId": 42,
"name": "Você",
"email": "voce@exemplo.com",
"username": "voce",
"locale": "pt-BR",
"timeZone": "America/Sao_Paulo",
"allowedTeamIds": [12],
"scopes": ["agent:read", "leads:read"]
}
}Base URL
Base recomendada:
https://api.useagendo.com/agent/v1Compatibilidade (rota original, segue funcionando):
https://app.useagendo.com/api/agent/v1Status público: GET https://api.useagendo.com/ → {"name":"Agendo Agent API","status":"ok","version":"v1"}
Autenticação (Agent API)
Todas as tools são POST em {base}/{tool} com o header Authorization e corpo JSON. Resposta sempre JSON.
// sucesso
{ "ok": true, "tool": "...", "mode": "read", "data": { ... } }
// erro
{ "ok": false, "tool": "...", "error": { "code": "...", "message": "...", "retryable": false } }Scopes
| Scope | Tools |
|---|---|
| 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, independente do acesso do usuário).
Erros
| code | HTTP | Quando |
|---|---|---|
| unauthorized | 401 | PAT ausente, inválido, expirado ou revogado. |
| forbidden_scope | 403 | Falta um scope exigido pela tool (ex.: write sem funnels:write). |
| forbidden_team | 403 | teamId fora do allowedTeamIds do PAT. |
| not_found | 404 | Recurso inexistente OU sem acesso (anti-enumeração). |
| validation_error | 400 | Input inválido (ex.: get_funnel sem funnelId/funnelUid). |
| rate_limited | 429 | Acima do limite por PAT. Tem header Retry-After. |
| internal | 500 | Erro inesperado (mensagem genérica, sem stack). |
| confirmation_required | 400 | Escrita com dryRun=false SEM confirmationToken (rode dryRun=true antes). |
| confirmation_invalid | 403 | confirmationToken inválido, payload trocado, ou já usado (replay bloqueado). |
| confirmation_expired | 400 | confirmationToken expirou (5 min). Rode dryRun de novo. |
| idempotency_conflict | 409 | Idempotency-Key reutilizada com payload diferente. |
| conflict | 409 | Conflito de estado (ex.: slug de funil já existe no escopo). |
Escrita (F1a)
Além de ler, a Agent API permite criar e editar rascunhos de funil com escrita controlada. Exige o scope funnels:write e segue um fluxo dry-run-first + confirmação: nada é gravado sem você confirmar.
Tools de escrita (MCP → Agent API):
create_funnel_draft→createFunnelDraft— Cria um rascunho de funil/formulário. Exige funnels:write. SEMPRE chame com dryRun=true primeiro.update_funnel_draft→updateFunnelDraft— Edita um funil/formulário existente (informe id). Exige funnels:write. SEMPRE dryRun=true primeiro.
Fluxo obrigatório:
- 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.
TOKEN="cal_agent_..." # PAT COM funnels:write
BASE="https://api.useagendo.com/agent/v1"
# 1) dry-run — NÃO grava; retorna preview + confirmationToken
curl -s -X POST "$BASE/createFunnelDraft" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Funil Black Friday","dryRun":true}'
# 2) executar — MESMO payload + dryRun:false + confirmationToken do passo 1.
# (Opcional) header Idempotency-Key pra evitar duplicar em retries.
curl -s -X POST "$BASE/createFunnelDraft" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9f1c-criar-bf" \
-d '{"name":"Funil Black Friday","dryRun":false,"confirmationToken":"<token-do-passo-1>"}'Garantias:
- Dry-run obrigatório — Toda escrita começa com dryRun=true. Sem ele você não tem confirmationToken pra executar.
- confirmationToken travado — Curto (expira em ~5 min) e preso ao SEU token + à tool + ao payload exato. Mudou o payload → confirmation_invalid.
- Não reutilizável (anti-replay) — Cada confirmationToken executa UMA vez. Reenviar o mesmo → confirmation_invalid.
- Idempotência — Header Idempotency-Key: mesma key + mesmo payload devolve a resposta anterior (não duplica); key + payload diferente → idempotency_conflict.
- Token read-only não escreve — Sem o scope funnels:write a tool retorna forbidden_scope.
- Auditoria — dryRun E exec viram um AgentRequest — sem payload cru/PII (só hash) e resposta sanitizada.
Exemplos (curl)
TOKEN="cal_agent_..."
BASE="https://api.useagendo.com/agent/v1"
# getMe
curl -s -X POST "$BASE/getMe" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -d '{}'
# listLeads (PII vem mascarada)
curl -s -X POST "$BASE/listLeads" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"teamId": 12, "limit": 20}'
# getFunnel
curl -s -X POST "$BASE/getFunnel" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"funnelId": 8}'URL do MCP
Servidor MCP remoto — Streamable HTTP stateless (JSON-RPC via POST):
https://mcp.useagendo.com/mcpMesma autenticação da Agent API: o PAT no header Authorization: Bearer cal_agent_…. O MCP só traduz as tools → chamadas da Agent API (leitura + escrita F1a); auth, escopo, máscara de PII, dry-run/ confirmação e auditoria ficam no núcleo.
Como conectar (MCP)
Genérico (clientes que leem config MCP — Cursor, Windsurf, etc.):
{
"mcpServers": {
"agendo": {
"url": "https://mcp.useagendo.com/mcp",
"headers": { "Authorization": "Bearer cal_agent_..." }
}
}
}Codex:
[mcp_servers.agendo]
url = "https://mcp.useagendo.com/mcp"
headers = { Authorization = "Bearer cal_agent_..." }Tools (MCP)
As 10 tools MCP (8 de leitura + 2 de escrita) mapeiam 1:1 para as tools da Agent API (camelCase):
| Tool MCP | Agent API | Descrição |
|---|---|---|
| get_me | getMe | Identidade do dono do PAT: usuário, scopes e times permitidos. |
| list_teams | listTeams | Times visíveis pelo PAT (restritos ao allowedTeamIds). |
| list_funnels | listFunnels | Lista funis do escopo. Sem teamId (ou null) = funis pessoais. |
| get_funnel | getFunnel | Detalhe de 1 funil + resumo das etapas. Informe funnelId OU funnelUid. |
| list_pixels | listPixels | Pixels Meta (CAPI) do escopo. Só hasToken — o token de acesso nunca sai. |
| get_pixel_health | getPixelHealth | Saúde de 1 pixel (último teste local). A entrega real é o Events Manager da Meta. |
| list_leads | listLeads | Lista leads do escopo. PII vem mascarada. Filtros opcionais e paginação. |
| get_lead | getLead | Detalhe de 1 lead (PII mascarada). Informe leadId OU leadUid. |
| create_funnel_draft | createFunnelDraft | Cria um rascunho de funil/formulário. Exige funnels:write. SEMPRE chame com dryRun=true primeiro. |
| update_funnel_draft | updateFunnelDraft | Edita um funil/formulário existente (informe id). Exige funnels:write. SEMPRE dryRun=true primeiro. |
Detalhe de input/output/exemplo de cada uma na Referência das tools.
Troubleshooting (MCP)
GET /mcp→405: o servidor é stateless; use POST (JSON-RPC). Não é erro.- Erro de auth na tool → revise o PAT (ver Suporte).
- Cliente não conecta → confirme que ele suporta MCP remoto / Streamable HTTP e aceita header Authorization.
Referência das tools
8 tools de leitura + 2 de escrita (F1a). Cada uma: endpoint da Agent API, scopes, input, output e um exemplo. As de escrita exigem funnels:write e o fluxo dry-run + confirmação (ver Escrita (F1a)).
get_me·Agent API: getMeIdentidade do dono do PAT: usuário, scopes e times permitidos.
- Endpoint
- POST https://api.useagendo.com/agent/v1/getMe
- Scopes
- agent:read
- Input
- Sem parâmetros.
- Output
- userId, name, email, username, locale, timeZone, allowedTeamIds, scopes
Exemplo
curl -s -X POST "https://api.useagendo.com/agent/v1/getMe" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{}'list_teams·Agent API: listTeamsTimes visíveis pelo PAT (restritos ao allowedTeamIds).
- Endpoint
- POST https://api.useagendo.com/agent/v1/listTeams
- Scopes
- agent:read
- Input
- Sem parâmetros.
- Output
- teams[]: teamId, name, slug, role
Exemplo
curl -s -X POST "https://api.useagendo.com/agent/v1/listTeams" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{}'list_funnels·Agent API: listFunnelsLista funis do escopo. Sem teamId (ou null) = funis pessoais.
- Endpoint
- POST https://api.useagendo.com/agent/v1/listFunnels
- Scopes
- agent:read + funnels:read
- Input
- teamId? (número | null)
- Output
- funnels[]: id, uid, name, teamId, enabled, pixelEnabled, leadsEnabled, stepCount
Exemplo
curl -s -X POST "https://api.useagendo.com/agent/v1/listFunnels" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "teamId": 12 }'get_funnel·Agent API: getFunnelDetalhe de 1 funil + resumo das etapas. Informe funnelId OU funnelUid.
- Endpoint
- POST https://api.useagendo.com/agent/v1/getFunnel
- Scopes
- agent:read + funnels:read
- 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
curl -s -X POST "https://api.useagendo.com/agent/v1/getFunnel" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "funnelId": 8 }'list_pixels·Agent API: listPixelsPixels Meta (CAPI) do escopo. Só hasToken — o token de acesso nunca sai.
- Endpoint
- POST https://api.useagendo.com/agent/v1/listPixels
- Scopes
- agent:read + pixels:read
- Input
- teamId? (número | null)
- Output
- pixels[]: id, teamId, name, pixelId, enabled, sendScope, statusEventMap, leadEventSource, lastTestStatus, hasToken
Exemplo
curl -s -X POST "https://api.useagendo.com/agent/v1/listPixels" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "teamId": 12 }'get_pixel_health·Agent API: getPixelHealthSaúde de 1 pixel (último teste local). A entrega real é o Events Manager da Meta.
- Endpoint
- POST https://api.useagendo.com/agent/v1/getPixelHealth
- Scopes
- agent:read + pixels:read
- Input
- pixelId (número, obrigatório)
- Output
- id, teamId, name, pixelId, enabled, sendScope, hasToken, lastTestedAt, lastTestStatus, note
Exemplo
curl -s -X POST "https://api.useagendo.com/agent/v1/getPixelHealth" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "pixelId": 5 }'list_leads·Agent API: listLeadsLista leads do escopo. PII vem mascarada. Filtros opcionais e paginação.
- Endpoint
- POST https://api.useagendo.com/agent/v1/listLeads
- Scopes
- agent:read + leads:read
- 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
curl -s -X POST "https://api.useagendo.com/agent/v1/listLeads" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "teamId": 12, "limit": 20 }'get_lead·Agent API: getLeadDetalhe de 1 lead (PII mascarada). Informe leadId OU leadUid.
- Endpoint
- POST https://api.useagendo.com/agent/v1/getLead
- Scopes
- agent:read + leads:read
- Input
- leadId? (número) | leadUid? (texto) — um obrigatório
- Output
- id, uid, name, emailMasked, phoneMasked, cpfMasked, status, source, value, teamId, qualification, createdAt
Exemplo
curl -s -X POST "https://api.useagendo.com/agent/v1/getLead" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "leadId": 4567 }'create_funnel_draft·Agent API: createFunnelDraftCria um rascunho de funil/formulário. Exige funnels:write. SEMPRE chame com dryRun=true primeiro.
- Endpoint
- POST https://api.useagendo.com/agent/v1/createFunnelDraft
- Scopes
- funnels:write
- 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
curl -s -X POST "https://api.useagendo.com/agent/v1/createFunnelDraft" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Funil Black Friday", "dryRun": true }'update_funnel_draft·Agent API: updateFunnelDraftEdita um funil/formulário existente (informe id). Exige funnels:write. SEMPRE dryRun=true primeiro.
- Endpoint
- POST https://api.useagendo.com/agent/v1/updateFunnelDraft
- Scopes
- funnels:write
- 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
curl -s -X POST "https://api.useagendo.com/agent/v1/updateFunnelDraft" \
-H "Authorization: Bearer cal_agent_..." \
-H "Content-Type: application/json" \
-d '{ "id": 8, "name": "Funil Black Friday 2", "enabled": false, "dryRun": true }'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 — 120 req/min por PAT (
429 rate_limitedcomRetry-After). - Permissões por time — o PAT só acessa os times do
allowedTeamIds. - Escrita com guarda — só funis (
funnels:write): dry-run obrigatório +confirmationToken(payload-locked, uso único/anti-replay) + idempotência opcional. Leads, Webhook e Pixel seguem somente leitura.
Suporte
Dúvidas ou problemas de integração? O ponto de partida é o app: https://app.useagendo.com.
- Recebeu
401 unauthorized→ revise o PAT (ausente, expirado ou revogado) e o header Authorization. - Recebeu
403 forbidden_scope→ o PAT não tem o scope exigido pela tool; gere um com o scope certo. - Recebeu
403 forbidden_team→ oteamIdestá fora doallowedTeamIdsdo PAT. - Recebeu
404 not_found→ recurso inexistente ou sem acesso (a API não distingue, por segurança).