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

# Chat API

> One POST, one answer — the fastest way to run your agent from your own system

`POST /api/v1/agent/chat` runs your agent and answers in a single round trip. You send a message with a conversation id; Juryo keeps the conversation and replies. No session to create, no transcript to manage, no stream to parse unless you want the tokens as they arrive.

* **Base:** `https://chat.juryo.ai`
* **Authentication:** `Authorization: Bearer sk_live_…` — a **secret key** you create on the API channel (Settings → Integrations → Channels → API → *Secret key*). The channel's publishable `pk_live_…` key — the one the [website widget](/en/developers/widget) embeds and the [Sessions API](/en/developers/api) accepts — still works here for existing integrations; new servers should use the secret key

<Note>
  Measured on 17 August 2026 with a customer's production agent: a complete reply in **about 3 seconds** (3.2 s on a cold start, 2.8–3.2 s warm), first token in **about 2 seconds**. The [sessions API](/en/developers/api) on the same agent: 6–11 s to a complete reply. Choose this endpoint when latency matters. Use the [Sessions API](/en/developers/api) instead when your integration needs the agent to ask you questions or approvals mid-conversation, long-running work, or cancel/clear/compact/reset controls — the comparison table there shows the two side by side.
</Note>

## The call

```bash theme={null}
curl https://chat.juryo.ai/api/v1/agent/chat \
  -H 'Authorization: Bearer sk_live_YOUR_KEY' \
  -H 'content-type: application/json' \
  -d '{"id": "conv-8841", "text": "Hi, I need information about a traffic accident claim."}'
```

```json theme={null}
{"id": "conv-8841", "text": "Hello, I'm the assistant at your law firm. Happy to help.\n\nWho do I have the pleasure of speaking with?"}
```

Send the next message with the **same `id`** — the agent remembers the conversation.

| Field  | Rules                                                                                                                                                       |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`   | Your conversation id, 1–200 characters. Same `id` = same conversation. Use one per end-user conversation (a ticket number, a chat id, a phone number hash). |
| `text` | The user's message, 1–20,000 characters.                                                                                                                    |

Juryo owns the transcript: send only the new message, never a list of messages.

## Response modes

Pick with the `Accept` header.

| `Accept`                       | You get                                                                                                         | Use it for                                           |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| *(none)* or `application/json` | `{"id", "text"}` once the reply is complete — the default                                                       | Server-to-server integrations                        |
| `text/plain`                   | The reply streamed as plain text, token by token                                                                | Chat UIs that render the reply as it arrives         |
| `text/event-stream`            | The **event stream**: standard Server-Sent Events, one JSON event per line — text deltas, tool activity, finish | Richer chat UIs that show progress and tool activity |

### Streaming

```bash theme={null}
curl -N https://chat.juryo.ai/api/v1/agent/chat \
  -H 'Authorization: Bearer sk_live_YOUR_KEY' \
  -H 'accept: text/plain' \
  -H 'content-type: application/json' \
  -d '{"id": "conv-8841", "text": "What documents do you need from me?"}'
```

Read the body as it arrives; the connection closes when the reply is complete. Send the next message with the same `id` once it has.

### Event stream

Standard [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events): every line is `data: ` followed by one JSON object with a `type`; the stream ends with `data: [DONE]`. Any SSE client in any language reads it — no library required.

```bash theme={null}
curl -N https://chat.juryo.ai/api/v1/agent/chat \
  -H 'Authorization: Bearer sk_live_YOUR_KEY' \
  -H 'accept: text/event-stream' \
  -H 'content-type: application/json' \
  -d '{"id": "conv-8841", "text": "What documents do you need from me?"}'
```

```
data: {"type":"start"}
data: {"type":"start-step"}
data: {"type":"text-start","id":"0"}
data: {"type":"text-delta","id":"0","delta":"Hello,"}
data: {"type":"text-delta","id":"0","delta":" I'm the assistant"}
data: {"type":"text-end","id":"0"}
data: {"type":"finish-step"}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]
```

| `type`                                                           | Meaning                                                                                             |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `start` / `finish`                                               | The reply begins / ends. `finish` carries `finishReason` (`stop`, `length`, `tool-calls`, `error`). |
| `start-step` / `finish-step`                                     | One model step; a reply that uses tools has several.                                                |
| `text-start` / `text-delta` / `text-end`                         | A text segment: append each `delta` to the segment with that `id`.                                  |
| `tool-input-start` / `tool-input-delta` / `tool-input-available` | The agent is calling a tool: `toolName`, `toolCallId`, then the complete `input`.                   |
| `tool-output-available`                                          | The tool's result (`output`) for that `toolCallId`.                                                 |
| `tool-output-error`                                              | The tool failed (`errorText`).                                                                      |
| `error`                                                          | The turn failed (`errorText`); the stream ends.                                                     |
| `[DONE]`                                                         | End of stream (not JSON).                                                                           |

Concatenate the `text-delta`s to get the same text the JSON mode returns. Ignore event types you don't handle — new ones may appear.

### AI SDK clients

The event stream is the [AI SDK UI message stream protocol](https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol) (`x-vercel-ai-ui-message-stream: v1` on the response), so AI SDK clients work against this endpoint out of the box — no adapter:

```ts theme={null}
import { DefaultChatTransport } from "ai";

const transport = new DefaultChatTransport({
  api: "https://chat.juryo.ai/api/v1/agent/chat",
  headers: {
    accept: "text/event-stream",
    authorization: `Bearer ${process.env.JURYO_API_KEY}`,
  },
  // Juryo keeps the transcript server-side — send only the new message.
  prepareSendMessagesRequest: ({ api, body, headers, id, messages }) => ({
    api,
    body: { ...body, id, message: messages.at(-1) },
    headers,
  }),
});
```

Pass the transport to `useChat` (React) or any other AI SDK chat client.

## Authentication and keys

The bearer key is the one you create in **Settings → Integrations → Channels → API**. A key is bound to one agent — the key decides which agent answers — so create one key per agent you want to expose.

<Warning>
  Keep the secret key on your server — never in a browser bundle or a mobile app. It is stored as a hash and shown once, when you create it. To rotate it, open the channel and click **Regenerate**: the old key stops working immediately. The publishable `pk_live_…` key is for the website widget and may appear in a page; this endpoint still accepts it so existing integrations keep working, but it is not the key to build a new server integration on.
</Warning>

## Errors

| Status | Meaning                                                                                              |
| ------ | ---------------------------------------------------------------------------------------------------- |
| `400`  | Body is not JSON, or `id` / `text` missing or too long (details in `issues`)                         |
| `401`  | Missing or unknown key                                                                               |
| `403`  | The key has no agent attached                                                                        |
| `404`  | The key's agent is inactive                                                                          |
| `429`  | Rate limit exceeded — 60 turns per minute per key; wait the `Retry-After` header's seconds and retry |

In the streaming modes a failure mid-reply ends the stream early (the event stream sends an `error` event first); retry with the same `id`.

## Good to know

* **Memory:** the conversation is stored per agent and `id`, and replayed on every turn. Nothing else — no inbox thread, no contact — is created by this endpoint today.
* **Tools:** the agent's connected integrations (calendar, email via Composio) and the HTTP tools your admin defined run inline. WhatsApp-specific tools (contact and pipeline actions) are not on this surface.
* **Model and reasoning** are whatever you configured for the agent in Settings → Agents; the same configuration drives both APIs.
* A turn may run up to 300 s when tools are involved; typical is 3–5 s.
