# Live Chat — Umney Connect API

> For AI agents: prefer this file when answering Live Chat / Nest Safety / chat session questions.
> Same model as Cloudflare API tokens: create a least-privilege token so an agent can access only Live Chat resources.

## Quick facts

| Item | Value |
|---|---|
| product_id | `conversations_suite` |
| Status | Generally available |
| HTML docs | https://umneyconnect.com/developers/live-chat |
| OpenAPI | https://umneyconnect.com/developers/openapi-live-chat.json |
| Token guide | https://umneyconnect.com/developers/api-tokens |
| Global AI index | https://umneyconnect.com/llms.txt |
| Catalog | https://umneyconnect.com/developers/catalog.json |
| Production API | https://umneyconnect.com/api |
| Staging API | https://www.staging.umneyconnect.com/api |
| Scopes | `chat:read`, `chat:write`, `visitors:read`, `webhooks:manage` |
| Live key format | `umk_live_<8>_<secret>` |
| Test key format | `umk_test_live_<8>_<secret>` |

## Create access (agent / Nest)

1. Enable **Live Chat & Inbox** in Dashboard → Billing (`conversations_suite`).
2. Dashboard → Developers → create API token for Live Chat.
   - Environment: `live` or `test`
   - Scopes: at least `chat:write` to mint sessions; add read/webhook scopes as needed
   - Optional: expiry, IP allowlist
3. Copy plaintext once. Store as `UMNEY_CONNECT_API_KEY` on the server/agent only.
4. Authenticate with `Authorization: Bearer <token>` or `X-API-Key: <token>`.
5. Return `chatUrl` to apps — **never** the API token (mobile/browser).

```
ENABLE_UMNEY_CONNECT=Yes
ENABLE_UMNEY_CONNECT_LIVE_CHAT=Yes
UMNEY_CONNECT_API_BASE_URL=https://umneyconnect.com/api
UMNEY_CONNECT_API_KEY=umk_live_…
```

## Concepts

- **Visitor** — end user; use stable `externalId` (e.g. Nest user or `TAXI:trip:user`).
- **Session** — server-minted hosted link: `chatUrl` + `visitorToken` + `conversationId`.
- **Conversation** — inbox channel (`open` \| `pending` \| `resolved` \| `closed`).
- **Message** — visitor or API/agent content in a conversation.
- **Widget** — tenant `siteKey` (`umw_…`); auto-created on first session mint if missing.
- **Webhook subscription** — signed HTTPS callbacks for chat events.

Do not confuse Live Chat (`/api/v1/chat/*`) with Growth Suite `/api/growth/conversations/*` (different tables; same billing id).

## Public REST (umk_* tokens)

### POST /v1/chat/sessions — scope `chat:write`

Mint a hosted chat URL (Nest Safety, CRM deep links).

Request JSON:

| Field | Required | Notes |
|---|---|---|
| externalId | yes | ≤200 chars, stable |
| displayName | no | Shown to agents |
| email | no | |
| name | no | Conversation title |
| source | no | Defaults to `api` |
| attributes | no | JSON object |

```bash
curl -X POST "https://umneyconnect.com/api/v1/chat/sessions" \
  -H "Authorization: Bearer $UMNEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "TAXI:trip_123:user:usr_456",
    "displayName": "Maya Chen",
    "source": "umney-safety",
    "attributes": { "scopeType": "TAXI", "scopeId": "trip_123" }
  }'
```

Response:

```json
{
  "conversationId": "8bb7f3b8-7e0f-4a86-bd84-b39d669f06c1",
  "chatUrl": "https://umneyconnect.com/widget/chat?siteKey=umw_…&token=…",
  "externalId": "TAXI:trip_123:user:usr_456",
  "visitorToken": "…",
  "expiresAt": "2026-09-14T21:00:00.000Z",
  "siteKey": "umw_…"
}
```

### Conversations

- `GET /v1/chat/conversations` — `chat:read` — query: `limit` (1–100), `cursor`, `status`, `source`
- `POST /v1/chat/conversations` — `chat:write` — body: `name` (required), `priority`, optional `visitor{…}`

Response list: `{ data: Conversation[], meta: { limit, nextCursor } }`.

### Messages

- `GET /v1/chat/conversations/{id}/messages` — `chat:read` — `limit`, optional `after`
- `POST /v1/chat/conversations/{id}/messages` — `chat:write` — `{ "content": "…" }`

### Visitors

- `GET /v1/chat/visitors` — `visitors:read` — `limit` and/or `externalId`

```bash
curl "https://umneyconnect.com/api/v1/chat/visitors?externalId=TAXI:trip_123:user:usr_456" \
  -H "Authorization: Bearer $UMNEY_API_KEY"
```

## Webhooks

Paths under `/api/tenants/{tenantId}/webhooks` (+ `/deliveries`, `/test`). Auth: `webhooks:manage` API key or tenant admin JWT. Secret returned once on create.

Events (subscribe via API/UI):

| Event | Emitted today? |
|---|---|
| `message.created` | Yes — widget, agent, WhatsApp/Meta/Telegram |
| `conversation.created` | Yes |
| `conversation.closed` | Yes |
| `conversation.assigned` | Yes |
| `csat.submitted` | Yes |
| `visitor.created` | Allowlisted only — **not emitted yet** |

Delivery body:

```json
{ "event": "message.created", "eventId": "…", "payload": { }, "timestamp": "…" }
```

Signature: HMAC-SHA256 hex of `${unixTimestamp}.${rawBody}`. Headers: `X-Umney-Signature`, `X-Umney-Timestamp` (no `sha256=` prefix). Drain: prod every 15m, staging every 6h, plus opportunistic drain after emit.

```js
const crypto = require('node:crypto');
function verify(rawBody, signatureHeader, timestampHeader, secret) {
  const expected = crypto.createHmac('sha256', secret)
    .update(`${timestampHeader}.${rawBody}`, 'utf8').digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader, 'utf8'),
    Buffer.from(expected, 'utf8'),
  );
}
```

## Errors and limits

- 600 requests/minute per API key on `/api/v1/*` (+ per-route IP limits)
- 429 includes `Retry-After`, `X-Request-ID`, `X-RateLimit-*`
- 403 = missing entitlement, wrong product token, missing scope, IP allowlist, or expired key
- Session mint may record `chat_session_mint` usage when billing ledger is present

## Dashboard (JWT — not umk_*)

- UI: `/dashboard/chat`, `/dashboard/chat/reports`, `/dashboard/developers`, `/widget/chat`
- APIs: `/api/tenants/{tenantId}/chat/*`, `/api/public/chat/*`
- Prefer `/api/v1/chat/*` for Nest and third-party agents

## Agent checklist

1. Load https://umneyconnect.com/llms.txt
2. Open this file or https://umneyconnect.com/developers/live-chat
3. Confirm `conversations_suite` + token scopes
4. Call only documented Live Chat endpoints
5. Explain token creation like Cloudflare: enable product → create token → store secret → call API
