> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jelou.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agente de IA externo

> Conecte um agente hospedado fora do Jelou e saiba exatamente o que ele recebe e o que deve responder

O nó **Agente de IA externo** delega a conversa a um agente que você hospeda fora do Jelou. Com o provedor **HTTP (contrato de turno do Jelou)** (`http_sync`), o Jelou faz um `POST` para a URL do seu agente em cada turno e espera uma resposta JSON com um formato exato. Este guia descreve esse formato, com exemplos que você pode copiar.

<Note>
  Este formato também vale para um agente HTTP implantado no Amazon Bedrock AgentCore: o que seu handler devolve é o que o Jelou valida.
</Note>

## O que seu agente recebe

O Jelou envia um `POST` com `content-type: application/json`, os headers da credencial que você configurou e, se você ativou, a assinatura da solicitação. O corpo é este:

```json theme={null}
{
  "turnId": "trn_01J8A7K2M4XQ",
  "inputId": "inp_9f3c2a71",
  "conversation": { "key": "wa:5511999999999", "channel": "whatsapp", "locale": "pt-BR" },
  "user": { "ref": "usr_8d41c0aa" },
  "input": { "parts": [{ "type": "text", "text": "Olá, quero saber meu saldo" }] },
  "context": { "variables": { "segmento": "premium" } },
  "deadlineMs": 25000,
  "trace": { "executionId": "exec_5b2e", "workflowId": "64083", "nodeId": "n_agent" },
  "interaction": "single_task"
}
```

| Campo | Descrição |
| - | - |
| `turnId` | Identificador do turno, `trn_` seguido de letras ou números. |
| `inputId` | Identificador da mensagem do usuário (1 a 128 caracteres). |
| `conversation` | `key`, `channel` (`whatsapp`, `facebook`, `instagram`, `web`, `email`, `sms`, `other`) e `locale` opcional. |
| `user` | `ref` pseudônimo (`usr_...`). `phone`, `name`, `email` e `legalId` só chegam se você os permitir em `inputMapping.sendUserFields`. |
| `input.parts` | De 1 a 10 partes. Uma mensagem de texto é `{"type":"text","text":"..."}`. |
| `input.inbound` | Somente em mensagens que não são texto (imagem, áudio, localização, resposta de Flow). |
| `context.variables` | Somente as variáveis do fluxo que você listar em `inputMapping.variables`. |
| `deadlineMs` | Milissegundos que seu agente tem para responder (1000 a 120000). |
| `trace` | `executionId` e, opcionalmente, `workflowId` e `nodeId`. |
| `interaction` | `single_task` ou `multi_turn`. |

## O que seu agente deve responder

Responda com HTTP 200 e um JSON. A resposta é **estrita**: qualquer campo que não esteja nesta tabela é rejeitado.

| Campo | Obrigatório | Descrição |
| - | - | - |
| `status` | Sim | `completed`, `action_required`, `failed`, `timed_out` ou `blocked`. |
| `outputs` | Sim | Array de até 20 partes. Use `[]` se não houver nada a dizer. |
| `signals` | Não | Até 5 sinais. Se você omitir, o Jelou usa `[]`. |
| `turnId` | Não | Se você omitir, o Jelou usa o da solicitação. Se enviar, deve ser idêntico ao recebido. |
| `provider` | Não | O Jelou preenche `provider.kind` por você. |
| `timing` | Não | O Jelou mede `timing.latencyMs` por você. |
| `usage` | Não | `inputTokens`, `outputTokens` e `reportedBy: "provider"`. |
| `error` | Somente com `failed` ou `timed_out` | `code`, `retryable` e `message` opcional (até 500 caracteres). Não é permitido com outro `status`. |

Regras por estado: `failed` e `timed_out` exigem `error` e `outputs: []`; `blocked` exige `outputs: []`; `action_required` exige um sinal `action_required`.

<Warning>
  Os nomes devem ser exatamente `status` e `outputs`. O Jelou não aceita aliases como `output`, `messages`, `result` ou `turnStatus`.
</Warning>

### Partes de `outputs`

| `type` | Campos |
| - | - |
| `text` | `text` obrigatório, de 1 a 4096 caracteres. Divida respostas mais longas em várias partes de texto. |
| `choices` | `choices` obrigatório, de 1 a 10 elementos `id` e `label` (o rótulo aceita até 24 caracteres). `text` opcional, até 1024. |
| `card` | `title` obrigatório (até 80). Opcionais: `body` (1024), `imageUrl` (https) e `actions` (até 3, com `label` e `url` ou `choiceId`). |
| `media` | `url` (https) e `mediaType` (por exemplo `image/png`); `caption` opcional. |
| `data` | `data`, um objeto. |

### Sinais (`signals`)

| `type` | Efeito |
| - | - |
| `handoff_requested` | Passa a conversa para uma pessoa. Campos opcionais: `reason` (até 500) e `queue`. O nó sai por **Transferir para um operador** (`agent_handoff`). |
| `conversation_ended` | Indica que a conversa terminou. |
| `variables_set` | `variables`, um objeto de 1 a 30 chaves que é salvo na variável do nó. |
| `action_required` | `actionId`, `name` e `arguments` opcional. |
| `action_selected` | `name` e `data` opcional. |

## Respostas de exemplo

### Resposta mínima

```json theme={null}
{ "status": "completed", "outputs": [{ "type": "text", "text": "Olá, com prazer te ajudo com seu saldo." }] }
```

É equivalente a enviar a forma completa:

```json theme={null}
{
  "turnId": "trn_01J8A7K2M4XQ",
  "status": "completed",
  "outputs": [{ "type": "text", "text": "Olá, com prazer te ajudo com seu saldo." }],
  "signals": [],
  "provider": { "kind": "http_sync" },
  "timing": { "latencyMs": 0 }
}
```

### Com botões de escolha

```json theme={null}
{
  "status": "completed",
  "outputs": [
    { "type": "choices", "text": "O que você precisa?", "choices": [{ "id": "saldo", "label": "Ver saldo" }, { "id": "asesor", "label": "Falar com atendente" }] }
  ]
}
```

### Passar para um atendente

```json theme={null}
{
  "status": "completed",
  "outputs": [{ "type": "text", "text": "Vou te transferir para um atendente." }],
  "signals": [{ "type": "handoff_requested", "reason": "O cliente pediu para falar com uma pessoa" }]
}
```

### Salvar variáveis e encerrar

```json theme={null}
{
  "status": "completed",
  "outputs": [{ "type": "text", "text": "Pronto, registrei sua solicitação. Obrigado por nos escrever." }],
  "signals": [
    { "type": "variables_set", "variables": { "ticket": "A-1042", "categoria": "reclamacao" } },
    { "type": "conversation_ended" }
  ]
}
```

### Erro do lado do agente

O nó sai por **Houve um erro** (`agent_failed`).

```json theme={null}
{
  "status": "failed",
  "outputs": [],
  "error": { "code": "provider_error", "retryable": true, "message": "O sistema de saldos não respondeu" }
}
```

## Códigos HTTP

Se seu agente responder com um código diferente de 2xx, o turno falha: `401` e `403` como `auth`, `404` e `410` como `not_active`, `429` como `rate_limited` e `408`, `504` ou `5xx` como `provider_error`. `429` e `5xx` são reenviados se você configurou tentativas.

## O que acontece se a resposta não cumpre o formato

O turno falha com o código `malformed` e a mensagem indica o que corrigir, apenas com nomes de campos e rotas (nunca com valores). Por exemplo:

```text theme={null}
response does not satisfy AgentTurnResult: missing status; missing outputs; unknown field output (did you mean outputs?); unknown field turnStatus (did you mean status?)
```

Você pode ver a mensagem em `externalAgent.error.message` no resultado do nó, na execução ou no teste.

## Exemplo: handler HTTP no Amazon Bedrock AgentCore (Python)

Lê a mensagem do usuário em `input.parts` e devolve uma resposta válida. O resultado da função é o corpo JSON da resposta.

```python theme={null}
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()


def answer_for(text):
    if "atendente" in text.lower():
        return {
            "status": "completed",
            "outputs": [{"type": "text", "text": "Vou te transferir para um atendente."}],
            "signals": [{"type": "handoff_requested", "reason": "O cliente pediu um atendente"}],
        }
    return {
        "status": "completed",
        "outputs": [{"type": "text", "text": "Recebi sua mensagem: " + text[:500]}],
    }


@app.entrypoint
def invoke(payload, context=None):
    parts = payload.get("input", {}).get("parts", [])
    text = " ".join(p.get("text", "") for p in parts if p.get("type") == "text").strip()
    try:
        result = answer_for(text)
    except Exception:
        return {
            "status": "failed",
            "outputs": [],
            "error": {"code": "provider_error", "retryable": True, "message": "O agente não conseguiu responder"},
        }
    result["turnId"] = payload["turnId"]
    return result


if __name__ == "__main__":
    app.run()
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.