Pular para o conteúdo principal

AI Agent

Atendente automatizado embarcado por phone, usando OpenAI Chat Completions com knowledge base (RAG via pgvector). Cada phone pode ter exatamente 1 AI Agent.

BYO-key: o gateway não inclui créditos OpenAI. Configure sua key via PATCH /v1/account/integrations/openai.

Base path: /v1/phones/:phoneId/ai-agent


Conceitos

  • Agent: configuração (system prompt, modelo, regras de handoff). 1 por phone.
  • Knowledge: documentos (URL, S3 file, texto inline) processados em chunks com embedding (text-embedding-3-small, 1536 dim). RAG retrieval top-K por turn.
  • Conversation: sessão por contactKey (chave canônica do contato). Persiste turnos para janela de contexto LLM.
  • Handoff: quando o agent transfere para humano (manual via API, ou automático por regra).

Configuração do agent

PUT /v1/phones/:phoneId/ai-agent

Cria ou atualiza (upsert) o agent.

Body

CampoTipoDefaultDescrição
systemPromptstring20-20.000 chars. Instruções, personalidade, escopo
modelstringgpt-4o-miniQualquer modelo OpenAI compatível com Chat Completions
enabledbooleanfalseLiga/desliga sem deletar
handoffRules.keywordsstring[]Frases que dispararam handoff manual (max 20)
handoffRules.afterTurnsintHandoff automático após N turnos
contextWindowTurnsint10Histórico máximo em prompt (1-50)
ragTopKint4Chunks RAG por turn (0-20; 0 = desabilita RAG)
conversationTtlDaysint30Auto-CLOSED após N dias sem turno
greetingstringPrimeiro contato envia esta mensagem antes do LLM
curl -X PUT https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../ai-agent \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"systemPrompt": "Você é a Vera, atendente da Acme Saúde...",
"model": "gpt-4o-mini",
"enabled": true,
"handoffRules": { "keywords": ["falar com humano", "atendente"], "afterTurns": 20 },
"contextWindowTurns": 10,
"ragTopK": 4,
"greeting": "Oi! Sou a Vera. Como posso ajudar?"
}'

Response 200 — objeto agent.

GET /v1/phones/:phoneId/ai-agent

Retorna configuração atual.

Errors

HTTPCodeQuando
404AGENT_NOT_FOUNDPhone sem agent configurado

DELETE /v1/phones/:phoneId/ai-agent

Remove agent + knowledge + conversations (cascade).

Response 204

POST /v1/phones/:phoneId/ai-agent/enable

Atalho para enabled=true sem precisar repassar todos os campos.

POST /v1/phones/:phoneId/ai-agent/disable

Atalho para enabled=false. Conversas em andamento continuam respondendo até a janela de contexto fechar.


POST /v1/phones/:phoneId/ai-agent/test

Sandbox para testar o prompt sem persistir conversa nem usar RAG.

Body

CampoTipoObrigatório
messagestringsim (1-4000 chars)
systemPromptOverridestringnão (max 20k chars)
curl -X POST https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../ai-agent/test \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "message": "Quanto custa uma consulta?" }'

Response 200

{
"reply": "Nossas consultas variam entre...",
"model": "gpt-4o-mini",
"usage": { "promptTokens": 89, "completionTokens": 124 }
}

Knowledge base

Base path: /v1/phones/:phoneId/ai-agent/knowledge

POST /v1/phones/:phoneId/ai-agent/knowledge

Ingere um documento (assíncrono — Worker baixa, extrai texto, chunka, gera embeddings).

Body (discriminated union por source)

{ "source": "URL", "url": "https://acme.com/sobre", "title": "Sobre a Acme" }
{ "source": "TEXT_INLINE", "text": "Conteúdo do FAQ ...", "title": "FAQ" }
{ "source": "S3_FILE", "s3Key": "knowledge/acme/manual.pdf", "title": "Manual" }
CampoTipoObrigatórioLimite
sourceenumsimURL, TEXT_INLINE, S3_FILE
urlURLURLHTTPS público
textstringTEXT_INLINE20-200.000 chars
s3KeystringS3_FILE3-500 chars
titlestringnão255 chars

Response 202

{
"id": "doc-1234-...",
"source": "URL",
"title": "Sobre a Acme",
"status": "PENDING",
"createdAt": "..."
}

Status: PENDINGPROCESSINGREADY ou FAILED (com errorReason).

GET /v1/phones/:phoneId/ai-agent/knowledge

Lista documentos.

[
{
"id": "doc-1234-...",
"source": "URL",
"sourceRef": "https://acme.com/sobre",
"title": "Sobre a Acme",
"status": "READY",
"chunkCount": 23,
"errorReason": null,
"createdAt": "...",
"updatedAt": "..."
}
]

GET /v1/phones/:phoneId/ai-agent/knowledge/:docId

Detalhe.

Errors

HTTPCodeQuando
404DOC_NOT_FOUND

DELETE /v1/phones/:phoneId/ai-agent/knowledge/:docId

Remove doc + chunks (cascade).

Response 204


Conversations

Conversas geradas automaticamente quando o agent responde uma mensagem inbound (1 por (phoneId, contactKey)).

Base path: /v1/phones/:phoneId/ai-agent/conversations

GET /v1/phones/:phoneId/ai-agent/conversations

Lista paginada.

Query

ParamTipoDefault
statusAI, HUMAN_HANDOFF, CLOSED
limitint50 (max 200)
cursorUUID

Response 200

{
"items": [
{
"id": "conv-1234-...",
"contactKey": "+5511988887777",
"status": "AI",
"turnCount": 3,
"handoffReason": null,
"handoffAt": null,
"lastTurnAt": "2026-05-21T14:33:00.000Z",
"closedAt": null,
"createdAt": "..."
}
],
"nextCursor": null
}

GET /v1/phones/:phoneId/ai-agent/conversations/:contactKey

Detalhe da conversa. :contactKey URL-encoded (ex.: %2B5511988887777).

Errors

HTTPCodeQuando
404CONVERSATION_NOT_FOUND

GET /v1/phones/:phoneId/ai-agent/conversations/:contactKey/messages

Histórico de turnos.

Query

ParamDefaultMax
limit50200

Response 200

[
{
"id": "msg-1234-...",
"conversationId": "conv-1234-...",
"role": "USER",
"content": "Quanto custa uma consulta?",
"tokensEstimate": 8,
"whatsappMessageId": "ABCD1234..."
},
{
"role": "ASSISTANT",
"content": "Nossas consultas variam entre...",
"tokensEstimate": 124,
"sentMessageId": "msg-out-...",
"retrievedChunkIds": ["chunk-1", "chunk-2"]
}
]

POST /v1/phones/:phoneId/ai-agent/conversations/:contactKey/handoff

Força transferência para humano. Agent para de responder.

Body

CampoTipoDefault
reasonstring"manual"
curl -X POST "https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../ai-agent/conversations/%2B5511988887777/handoff" \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "reason": "cliente_pediu" }'

Response 200 — conversa atualizada. Também publica evento agent:handoff (ver Webhooks).

POST /v1/phones/:phoneId/ai-agent/conversations/:contactKey/release

Devolve para a IA (volta de HUMAN_HANDOFF para AI). Não funciona se já está CLOSED.

Response 200 — conversa atualizada.

Errors

HTTPCodeQuando
409CONVERSATION_CLOSED

POST /v1/phones/:phoneId/ai-agent/conversations/:contactKey/close

Fecha a conversa (status: CLOSED + closedAt). Idempotente.

Response 200


Auto-handoff

O agent decide automaticamente quando transferir, baseado em:

  • Keywords: se o último user message contém qualquer palavra de handoffRules.keywords (match case-insensitive substring).
  • afterTurns: se turnCount >= afterTurns.

Quando dispara, publica agent:handoff com manual: false.


Stateless endpoints (sem agent persistente)

Para casos onde você quer LLM mas não quer instalar agent:

POST /v1/ai/summary

Sumariza uma lista de mensagens.

Body

{
"messages": [
{ "from": "user", "text": "Oi, quero saber sobre o produto" },
{ "from": "assistant", "text": "Claro! Temos várias opções..." }
],
"format": "bullet",
"language": "pt_BR"
}
CampoTipoDefault
messagesarray1-200 itens
messages[].fromuser | assistantsim
messages[].textstring1-8000 chars
formatbullet | paragraphbullet
languagestringpt_BR
modelstringgpt-4o-mini

Response 200

{
"summary": "- Cliente perguntou sobre produto X\n- Atendente apresentou 3 opções...",
"keyTopics": ["produto X", "preço", "garantia"],
"openQuestions": ["forma de pagamento"],
"model": "gpt-4o-mini",
"usage": { "promptTokens": 134, "completionTokens": 87 }
}

POST /v1/ai/classify

Classifica um texto em uma de N categorias.

Body

{
"text": "Quero saber o status do meu pedido 12345",
"categories": ["vendas", "suporte_tecnico", "rastreio_pedido", "reclamacao"],
"language": "pt_BR"
}
CampoTipoLimite
textstring1-8000 chars
categoriesstring[]2-20 itens, cada item 1-60 chars

Response 200

{
"category": "rastreio_pedido",
"confidence": 0.95,
"alternatives": [],
"rationale": "Cliente mencionou número de pedido explicitamente",
"model": "gpt-4o-mini",
"usage": { "promptTokens": 67, "completionTokens": 38 }
}

Errors

HTTPCodeQuando
502AI_BAD_OUTPUTOpenAI retornou JSON inválido

Custos

Todas as chamadas (test, agent turns, summary, classify, knowledge embedding) consomem da sua key OpenAI direto. O gateway registra em ai_usage_events para você consultar via admin endpoints, mas não cobra markup.

Estimativa típica (gpt-4o-mini):

  • Agent turn: ~200-800 tokens prompt + ~100-300 completion = ~$0.0002-0.0008 por turn.
  • Embedding (knowledge): ~$0.00002 por chunk.
  • Whisper: ~$0.006/min.