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

# API Access for AI Agents

> How an AI agent talks to an Animam-powered chatbot — agent-to-agent over plain HTTP.

When an external AI agent talks to a website running Animam, **it does not get direct access to the site's tools**. It talks to the Animam chatbot agent, and the chatbot orchestrates the tools (corpus lookup, calendar booking, payment, product recommendations, etc.) on its behalf.

This is the only public agent surface. There is no separate `tools/list` endpoint exposed to anonymous callers — the chatbot is the agent.

## Endpoint

```
POST https://api.animam.ai/chat/{slug}
```

No authentication required for tenants that have public chat enabled (default for paid plans). The widget-key gate, if configured, still applies.

## Declaring you are an agent

Send the `X-Agent-Model` header so the chatbot knows it's talking to another agent (not a human). It tags the conversation, switches the tool channel to `api`, and triggers the **structured JSON envelope** response automatically.

```bash theme={null}
curl -X POST https://api.animam.ai/chat/acme-corp \
  -H "Content-Type: application/json" \
  -H "X-Agent-Model: claude-opus-4-7" \
  -d '{
    "message": "What are your business hours?",
    "sessionId": "agent-session-abc123"
  }'
```

Fallback when you cannot set headers: prefix the first message with `[AGENT:model-name]` — the chatbot strips it out and treats the rest as the question.

```json theme={null}
{
  "message": "[AGENT:gpt-4o] What are your business hours?",
  "sessionId": "agent-session-abc123"
}
```

## Response shape (agent-aware)

When `X-Agent-Model` is present and `stream` is not explicitly set, you get a single JSON response. No SSE parsing.

```json theme={null}
{
  "message": "We're open Monday to Friday, 9am to 6pm. Anything else I can help with?",
  "sessionId": "agent-session-abc123",
  "conversationId": "d67e62ae-7b5c-405f-9bec-f1352d3de988",
  "messageCount": 2,
  "visitorType": "agent",
  "agentModel": "claude-opus-4-7",
  "toolExecutions": [
    {
      "name": "explore_corpus",
      "type": "EXPLORE_CORPUS",
      "success": true,
      "summary": "Found 1 entry: business-hours",
      "data": { "entries": [{ "id": "...", "title": "Business hours" }] },
      "timestamp": "2026-05-05T14:32:11.420Z"
    }
  ],
  "contactSubmitted": false,
  "llmSource": "managed",
  "llmProvider": "anthropic",
  "llmModel": "claude-haiku-4-5"
}
```

| Field              | Type                                                        | Description                                                                                   |
| ------------------ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `message`          | string                                                      | Prose reply from the chatbot                                                                  |
| `sessionId`        | string                                                      | Echoed back. Reuse it on the next call to keep context                                        |
| `conversationId`   | string                                                      | Canonical conversation handle (DB id)                                                         |
| `messageCount`     | number                                                      | Total messages in this conversation                                                           |
| `visitorType`      | `"agent"` \| `"human"`                                      | Resolved from `X-Agent-Model` header / `[AGENT:]` prefix                                      |
| `agentModel`       | string \| undefined                                         | Echo of what the caller declared (truncated to 100 chars). **Declarative** — not verified     |
| `toolExecutions`   | array \| `[]`                                               | Tools the chatbot invoked while building the reply. Sanitized: no raw inputs                  |
| `contactSubmitted` | boolean                                                     | `true` if a `SUBMIT_FORM` tool was successfully called                                        |
| `toolResults`      | object \| undefined                                         | Map of `toolName → success` for each tool invoked                                             |
| `status`           | `"escalated"` \| undefined                                  | Set when the chatbot escalated to a human                                                     |
| `llmSource`        | `"managed"` \| `"passthrough"` \| `"stored"` \| `"webhook"` | Where the LLM key came from                                                                   |
| `llmProvider`      | string                                                      | `"anthropic"`, `"openai"`, `"mistral"`, …                                                     |
| `llmModel`         | string \| undefined                                         | The exact model that generated the reply (e.g. `claude-haiku-4-5`). Absent for `webhook` mode |

The HTTP response also carries `X-AI-Generated: true` (AI Act Art. 50 transparency).

## Tool executions, not tool calls

`toolExecutions[]` reports what the chatbot *did internally* — not actions you can invoke. The chatbot decides which tools to use based on the question.

If the chatbot collects a contact via `SUBMIT_FORM`, books a meeting via `BOOK_MEETING`, or charges via `COLLECT_PAYMENT`, the agent caller sees it in `toolExecutions[]` with structured `data`. Useful for:

* attribution (the agent caller wrote the lead, not a random visitor)
* follow-up logic (e.g., the agent caller polls a CRM after seeing `submission.created`)
* debugging (which tool ran, did it succeed, what did it return)

Raw inputs (the parameters the chatbot passed to the tool) are intentionally *not* in the envelope to avoid leaking PII the chatbot extracted from the conversation.

## Multi-turn conversations

Reuse the returned `sessionId` (or `conversationId`):

```python theme={null}
import requests

URL = "https://api.animam.ai/chat/acme-corp"
HEADERS = {"X-Agent-Model": "claude-opus-4-7", "Content-Type": "application/json"}

r1 = requests.post(URL, headers=HEADERS, json={
    "message": "Do you have an enterprise plan?",
    "sessionId": "agent-session-xyz"
})
data = r1.json()

r2 = requests.post(URL, headers=HEADERS, json={
    "message": "Does it include SSO?",
    "sessionId": "agent-session-xyz"
})
```

The chatbot keeps full context across messages with the same `sessionId`.

## Streaming (opt-in for agents)

Streaming is the default for browsers but opt-in for agents. Set `stream: true` to receive SSE chunks, ending with a `done` event that carries the same envelope fields.

```bash theme={null}
curl -N -X POST https://api.animam.ai/chat/acme-corp \
  -H "Content-Type: application/json" \
  -H "X-Agent-Model: claude-opus-4-7" \
  -d '{"message": "Tell me about your product.", "sessionId": "...", "stream": true}'
```

```
data: {"text": "We "}
data: {"text": "build "}
data: {"text": "AI infrastructure..."}
data: {"done": true, "sessionId": "...", "conversationId": "...", "visitorType": "agent", "agentModel": "claude-opus-4-7", "toolExecutions": [], "llmSource": "managed", "llmProvider": "anthropic", "llmModel": "claude-haiku-4-5"}
```

## Discovery before talking

Before sending the first message, an agent can introspect the tenant:

```bash theme={null}
curl https://api.animam.ai/discover/{slug}
```

Returns bot persona, segments, capabilities, public welcome message — useful to decide which `segment` to target.

## Rate limits

| Window   | Per-IP limit |
| -------- | ------------ |
| 1 minute | 20 messages  |
| 1 hour   | 200 messages |

When exceeded the API returns HTTP 429 with `Retry-After`.

## Errors

| HTTP | Body                                                                                       | Meaning                                         |
| ---- | ------------------------------------------------------------------------------------------ | ----------------------------------------------- |
| 400  | `{ "error": "message and sessionId are required" }`                                        | Missing required field or message > 15000 chars |
| 403  | `{ "error": "Origin not allowed" }`                                                        | Tenant restricts allowed origins                |
| 403  | `{ "error": "Invalid or missing widget key" }`                                             | Tenant requires a widget key                    |
| 404  | `{ "error": "Tenant not found" }`                                                          | Bad slug                                        |
| 402  | `{ "error": "Plan not activated", "code": "NOT_ACTIVATED" }`                               | Tenant on a plan without widget capability      |
| 429  | `{ "error": "Conversation limit reached", "code": "LIMIT_REACHED", "nextResetAt": "..." }` | Monthly conversation cap hit                    |

## Integration examples

### Python — agent-to-agent

```python theme={null}
import requests

def ask_animam_agent(slug: str, question: str, session_id: str, my_model: str) -> dict:
    r = requests.post(
        f"https://api.animam.ai/chat/{slug}",
        headers={"X-Agent-Model": my_model, "Content-Type": "application/json"},
        json={"message": question, "sessionId": session_id},
    )
    r.raise_for_status()
    return r.json()  # full envelope

reply = ask_animam_agent("acme-corp", "Pricing for 50 seats?", "sess-1", "claude-opus-4-7")
print(reply["message"])
print("Tools used:", [t["name"] for t in reply.get("toolExecutions", [])])
print("Generated by:", reply.get("llmModel"))
```

### LangChain Tool

```python theme={null}
from langchain.tools import Tool
import requests

def acme_chatbot(query: str) -> str:
    r = requests.post(
        "https://api.animam.ai/chat/acme-corp",
        headers={"X-Agent-Model": "claude-opus-4-7", "Content-Type": "application/json"},
        json={"message": query, "sessionId": "langchain-session"},
    )
    return r.json()["message"]

acme_tool = Tool(
    name="AcmeCorpAgent",
    description="Talk to Acme Corp's customer-facing AI agent. It can answer product questions, recommend products, book meetings, collect leads.",
    func=acme_chatbot,
)
```

## Next steps

<CardGroup cols={2}>
  <Card title="Agent-Ready" icon="robot" href="https://animam.ai/agent-ready">
    Full agent-readiness layers (.well-known files, MCP, JSON-LD)
  </Card>

  <Card title="Discovery" icon="compass" href="/api-reference/endpoints/tenant">
    Introspect a tenant before chatting
  </Card>
</CardGroup>
