# Prosody Agent API and Web Integration

Build an agent in Prosody Console, publish the skillbook, and attach it to your own product through a server-side API call.

## What You Can Integrate

Prosody agents turn uploaded CS manuals, FAQ documents, policy documents, and builder instructions into a runtime skillbook. A deployed agent can answer end-user questions with multi-turn context, retrieve matching knowledge from the skillbook, return handoff signals, and keep a compact conversation state that your application can store between turns.

The public integration surface is intentionally simple:

1. Create or select an agent in **Agent Builder**.
2. Upload documents and build the skillbook.
3. Review and edit the skillbook.
4. Publish the skillbook.
5. Create an API key.
6. Call `POST /api/v1/agents/chat` from your server.

Do not call Prosody directly from browser JavaScript with a private API key. Treat the key like a backend secret.

## Base URL

```txt
https://console.humelo.com
```

For local development, use your own server as a proxy and keep the Prosody API key in your server environment.

## Authentication

Agent runtime calls use Bearer authentication.

```http
Authorization: Bearer pk_live_your_api_key
Content-Type: application/json
```

Create an API key in **API Key** or from the Agent Builder API Key action. The plain key is shown once, so store it in a secret manager.

## Endpoint

```http
POST /api/v1/agents/chat
```

This endpoint answers one agent conversation turn. Send the current user message, recent history, and the latest state returned by the previous response.

## Request Body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `agent_id` | string | Yes | Agent ID from the agent you want to run. |
| `message` | string | Recommended | Latest user message. If omitted, Prosody uses the last user item from `messages` or `history`. |
| `history` | array | No | Recent turns, each with role `user` or `assistant` and a `content` string. |
| `messages` | array | No | Alias for `history`; useful when your app already uses chat-completion style naming. |
| `state` | object | No | Compact runtime state returned by the previous response. Store and send it back on the next turn. |
| `stream` | boolean | No | Set `true` to receive Server-Sent Events. You can also send `Accept: text/event-stream`. |

## Minimal cURL

```bash
curl https://console.humelo.com/api/v1/agents/chat \
  -H "Authorization: Bearer ${PROSODY_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "00000000-0000-0000-0000-000000000000",
    "message": "예약 취소 수수료가 어떻게 되나요?",
    "history": []
  }'
```

## JSON Response

```json
{
  "agent_id": "00000000-0000-0000-0000-000000000000",
  "agent_name": "라온투어 고객센터",
  "response": "예약 조건에 따라 취소 수수료가 달라질 수 있습니다. 예약번호와 출발일을 알려주시면 확인 기준을 안내드리겠습니다.",
  "matched_intent": "패키지 취소료 기준",
  "handoff_required": false,
  "retrieved_context": [
    {
      "title": "패키지 취소료 기준",
      "content": "여행 개시일 기준 취소 시점별 수수료 규정..."
    }
  ],
  "state": {
    "topic": "cancellation_fee",
    "handoff": false
  },
  "runtime": {
    "mode": "text",
    "voice_engine": "DIVE"
  }
}
```

## Streaming Response

Set `stream: true` or send `Accept: text/event-stream`.

```bash
curl https://console.humelo.com/api/v1/agents/chat \
  -N \
  -H "Authorization: Bearer ${PROSODY_API_KEY}" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "agent_id": "00000000-0000-0000-0000-000000000000",
    "message": "환불은 얼마나 걸리나요?",
    "stream": true
  }'
```

SSE events:

| Event | Payload | Meaning |
| --- | --- | --- |
| `start` | `matched_intent`, `handoff_required`, `retrieved_context`, `state` | Runtime matched the skillbook and started generation. |
| `chunk` | `delta` | A piece of assistant text. Append it to the visible answer. |
| `done` | `response`, `matched_intent`, `handoff_required`, `retrieved_context`, `state` | Final response and state for the next turn. |
| `error` | `error` | Runtime could not complete the turn. Show a fallback and log the request. |

## Next.js Server Proxy

Use this shape when you want a ChannelTalk-style widget on your website. The browser talks to your backend. Your backend calls Prosody.

```ts
// app/api/agent/chat/route.ts
export async function POST(request: Request) {
  const body = await request.json();

  const response = await fetch("https://console.humelo.com/api/v1/agents/chat", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.PROSODY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      agent_id: process.env.PROSODY_AGENT_ID,
      message: body.message,
      history: body.history ?? [],
      state: body.state ?? null,
      stream: false,
    }),
  });

  return new Response(await response.text(), {
    status: response.status,
    headers: { "Content-Type": response.headers.get("Content-Type") ?? "application/json" },
  });
}
```

## Browser Widget Pattern

A production widget usually needs four pieces:

1. A launcher button fixed to the bottom corner.
2. A chat panel that stores local draft messages.
3. A server-side proxy endpoint.
4. A small session store that preserves `history` and Prosody `state`.

Minimal client shape:

```ts
type Turn = { role: "user" | "assistant"; content: string };

let history: Turn[] = [];
let state: Record<string, unknown> | null = null;

async function sendToAgent(message: string) {
  const response = await fetch("/api/agent/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message, history, state }),
  });

  if (!response.ok) {
    throw new Error("Agent is temporarily unavailable");
  }

  const result = await response.json();
  history = [
    ...history,
    { role: "user", content: message },
    { role: "assistant", content: result.response },
  ].slice(-16);
  state = result.state ?? state;
  return result;
}
```

## Multi-Turn Rules

Prosody does not require you to resend the full conversation forever. Send a short recent history window and the returned `state`. The state is designed to preserve compact task context such as current topic, handoff flow, and useful runtime hints.

Recommended defaults:

- Keep the last 8 to 16 turns in `history`.
- Persist `state` per user conversation.
- Start a new state when the user intentionally starts a new support session.
- Do not include sensitive internal notes that should not be used by the agent.

## Handoff Handling

If `handoff_required` is `true`, show a human-support path. You can still display `response`; it is written to explain what information is needed before escalation.

```ts
if (result.handoff_required) {
  openHumanSupport({
    transcript: history,
    state: result.state,
    matchedIntent: result.matched_intent,
  });
}
```

## Security Checklist

- Keep `PROSODY_API_KEY` on the server only.
- Restrict your proxy with your own session, origin, or rate limit policy.
- Never put the API key in a script tag, public environment variable, or mobile bundle.
- Log Prosody request IDs or your own correlation IDs for support.
- Redact PII before storing transcripts if your policy requires it.

## Error Handling

| Status | Meaning | Recommended handling |
| --- | --- | --- |
| 400 | Missing or malformed request | Fix request shape before retrying. |
| 401 | Invalid API key | Rotate or reconfigure the key. |
| 404 | AICC not found or not owned by the API key organization | Check `agent_id` and organization ownership. |
| 500 | Runtime error | Show temporary fallback and retry later. |

## LLM/Agent Context

Use the following files when asking ChatGPT, Claude, Codex, or another coding agent to integrate Prosody AICC:

- `/llms.txt`: documentation index.
- `/llms-full.txt`: all available documentation in one text file.
- `/docs-md/aicc`: this page as Markdown.

Prompt example:

```txt
Use the Prosody AICC documentation from /docs-md/aicc.
Implement a server-side proxy and a browser chat widget.
Do not expose PROSODY_API_KEY to the browser.
```
