---
title: "Messaging API"
description: "Verified conversations REST under /api/conversations — channel types, metadata.kind, inbox hide, Tunnel fan-out"
locale: "en"
---
# Messaging API

Ring Platform messaging combines **REST routes under `/api/conversations`** with **Tunnel Protocol** for live message and typing events. Conceptual SSOT for channel vs subtype: [Real-Time Messaging](/docs/features/messaging.md).

> **Info**
> Use **Founder** / **Developer** tabs in the docs sidebar to filter this page. Older drafts invented a `channel` conversation type and fabricated SQL — this page matches verified routes and `features/chat/types`.

## Conversation.type (verified)

Create and filter use this closed channel enum only:

`direct` | `entity` | `opportunity` | `product` | `group`

| Field | Role |
|-------|------|
| `type` | Channel / routing |
| `metadata.kind` | Optional subtype (e.g. `generative_gallery`) — open `string` today |
| `metadata.hiddenFromInbox` | Hide from Messages list when `true` |

Do **not** send `type: 'channel'` or `type: 'generative'`. Tool editors reuse `product` + kind + hide flags — see [Generative Gallery](/docs/features/generative-media.md).

`GET /api/conversations` ultimately lists via `ConversationService.getConversations`, which skips `isHiddenToolConversation` rows (hidden tool chats never pollute the inbox).

### For founders

## What operators get

- Members chat in context of deals, entities, and products without leaving your Ring.
- Generative media history stays in hidden tool threads so Messages stays for people and product agents.
- Live updates use Tunnel Protocol (WebSocket on k8s; SSE/poll on Vercel).

  
- **[Real-Time Messaging](/docs/features/messaging.md)** — Channel vs kind taxonomy and inbox hygiene.

  
- **[Tunnel Protocol](/docs/features/tunnel-protocol.md)** — WSS / SSE / poll transports.

  
- **[Generative Gallery](/docs/features/generative-media.md)** — Hidden `genmedia:` product-tool chats.

### For developers

## Transport

- Server: `publishToChannel('conversation:' + id, …)` after writes (`lib/tunnel/publisher`)
- Client: **`useTunnelChannel`** (`hooks/use-tunnel-channel.ts`) — not raw `useTunnel().subscribe()` in effects
- **k8s / self-hosted:** native WSS `/api/tunnel/ws` primary
- **Vercel:** SSE + long-poll `/api/tunnel/poll`
- Optional Supabase Realtime when configured

## Verified routes

| Method | Path | Role |
|--------|------|------|
| GET | `/api/conversations` | List conversations (filters; tool chats excluded) |
| POST | `/api/conversations` | Create conversation |
| GET | `/api/conversations/[id]` | Conversation detail |
| PUT | `/api/conversations/[id]` | Update conversation |
| DELETE | `/api/conversations/[id]` | Delete / leave (see route) |
| GET, POST | `/api/conversations/[id]/messages` | List / send messages |
| POST, GET | `/api/conversations/[id]/typing` | Typing signal |
| POST, GET | `/api/conversations/[id]/read` | Mark / read state |
| POST | `/api/conversations/upload` | Attachment upload |
| POST | `/api/conversations/[id]/call-invite` | WebRTC invite (direct only) |
| POST | `/api/conversations/[id]/call-event` | Call signaling event |

### `GET /api/conversations`

Query params accepted by the route include `type` (cast to the verified enum above), plus other filter keys wired to `ConversationFilters` / pagination. Prefer reading `app/api/conversations/route.ts` for the exact query map — do not assume legacy `status=channel` samples.

List results omit conversations where `isHiddenToolConversation` is true (`hiddenFromInbox`, `kind === 'generative_gallery'`, or legacy `genmedia:` / `imggen:` prefixes on `productId` / `subject`).

### `POST /api/conversations`

Zod create schema (`app/api/conversations/route.ts`):

- `type` — required enum: `direct` | `entity` | `opportunity` | `product` | `group`
- `participantIds` — non-empty string array
- `metadata` — optional object; **required** when `type !== 'direct'`
- Domain rules: `entityId` for entity, `opportunityId` for opportunity, `productId` for product, `groupName` (min length) for group
- Optional tool fields on metadata: `kind`, `hiddenFromInbox`, `subject`, `vendorId`, …

{`{
  type: 'product',
  participantIds: [userId],
  metadata: {
    productId: 'genmedia:nft:draft:nft-create:mint-asset',
    productName: 'Generative media editor',
    subject: 'genmedia:nft:draft:nft-create:mint-asset',
    kind: 'generative_gallery',
    hiddenFromInbox: true,
  },
}`}

### Messages

| Method | Path | Notes |
|--------|------|-------|
| GET | `/api/conversations/[id]/messages` | Cursor pagination: `limit`, `cursor`, `direction` |
| POST | `/api/conversations/[id]/messages` | Body: content + optional `type`, attachments, message `metadata` |

Message `type` values in chat types: `text` | `image` | `file` | `system` | `payment_request` | `env_request` | `task` | `poll` | `rsvp` | `dao_jar` | `share_card` (SSOT: `MESSAGE_TYPE_ALLOWLIST` in `features/chat/lib/interactive-kind.ts`). Message `metadata.kind` (e.g. `payment_request`, `gallery_upload`) is a **different** namespace from conversation `metadata.kind`.

### Modules

| Path | Role |
|------|------|
| `features/chat/types/index.ts` | `Conversation`, `CreateConversationRequest`, `ConversationFilters` |
| `features/chat/services/conversation-service.ts` | List/create + `isHiddenToolConversation` |
| `features/chat/services/message-service.ts` | Messages |
| `app/api/conversations/**` | HTTP surface |
| `hooks/use-tunnel-channel.ts` | Client subscribe SSOT |

## Related

  
- **[Real-Time Messaging](/docs/features/messaging.md)** — Feature taxonomy and operator scenarios.

  
- **[Tunnel Protocol](/docs/features/tunnel-protocol.md)** — Transport SSOT.

  
- **[Generative Gallery](/docs/features/generative-media.md)** — Hidden product-tool chats.

  
- **[WebRTC Calls](/docs/features/webrtc-calls.md)** — Direct-chat call invite routes.

  
- **[Architecture — Real-time](/docs/architecture/real-time.md)** — Broader realtime topology.
