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

# Inbox

> List WhatsApp conversations, fetch messages, and reply with POST /v1/messages.

LazyApp's inbox is WhatsApp direct messages only — not Instagram comments, Facebook reviews, or a multi-network unified feed. Conversations are **1:1 threads** between a [connection](https://docs.lazyapp.in/concepts/glossary.md#connection) and a [contact](https://docs.lazyapp.in/concepts/glossary.md#contact). Groups are separate — see [Group chats](https://docs.lazyapp.in/guides/groups.md).

Unlike Zernio's conversation-centric inbox (`POST /v1/inbox/conversations/.../messages`), LazyApp is **recipient-centric**: you list threads with `/v1/conversations`, then **send with [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages)** addressed to the contact's phone (or BSUID). Conversations are created automatically when messages flow.

> [!NOTE]
>
> Building an inbox **inside your SaaS for each tenant**? Use the same endpoints with a customer-scoped key, then read [Tenant inbox](https://docs.lazyapp.in/platform/inbox.md) for webhook-first architecture and [embedded components](https://docs.lazyapp.in/platform/embedded-components.md).

## Feature matrix

| Feature | LazyApp |
| - | - |
| List conversations | Yes — [`GET /v1/conversations`](https://docs.lazyapp.in/api-reference/conversations/list-conversations) |
| Fetch messages in a thread | Yes — [`GET /v1/conversations/{id}/messages`](https://docs.lazyapp.in/api-reference/conversations/list-messages-in-a-conversation) |
| Send text / media / interactive | Yes — [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages) |
| Quote a specific message | Yes — `context.message_id` (Meta `wamid`) |
| React with emoji | Yes — `type: reaction` on [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages#types) |
| Commerce (product, catalog, carousel) | Yes — [Catalog](https://docs.lazyapp.in/sending/catalog.md); session messages only |
| Location & contact cards | Yes — message `type` `location` / `contacts` |
| Typing indicator | Yes — [`POST /v1/conversations/{id}/typing`](https://docs.lazyapp.in/guides/inbox#typing) |
| Archive / pin thread | Yes — workspace-level [`PATCH /v1/conversations/{id}`](https://docs.lazyapp.in/guides/inbox#archive-and-pin) (`archived` / `pinned`). Distinct from per-agent console pins |
| Voice notes (`voice: true`) | Yes — `type: audio` with `voice: true` on [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages#types) |
| Delete outbound message via API | **Not built** — `message.deleted` covers coexistence revokes from the phone app |
| Create conversation then post into it | **Skip** — send to the recipient; LazyApp attaches the conversation |
| Search conversations | Yes — `q` on [`GET /v1/conversations`](https://docs.lazyapp.in/guides/inbox#list-conversations) |

## Direct messages

### List conversations

Most recently active first. Pinned threads sort first. Filter with `status` (`open`, `pending`, `resolved`, `closed`), `assignee_id`, or `archived`. Search with `q` (contact phone / username / BSUID / external id, or latest message body). Cursor-paginated (`limit` 1–100, default 25).

```bash theme={null}
curl "https://lazyapp.in/v1/conversations?limit=50&status=open&q=9812" \
  -H "Authorization: Bearer $LAZYAPP_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "cnv_…",
      "status": "open",
      "archived": false,
      "pinned": true,
      "contact": {
        "uuid": "…",
        "phone_number": "+919812345670",
        "bsuid": null
      },
      "assignee": { "id": 12, "name": "Asha" },
      "messages_count": 14,
      "last_message_at": "2026-09-01T18:30:00+00:00",
      "last_inbound_at": "2026-09-01T18:29:00+00:00"
    }
  ],
  "next_cursor": "eyJpZCI6MTIzfQ"
}
```

### Typing

Shows Meta's typing indicator for the latest inbound message in the thread.

```bash theme={null}
curl -X POST https://lazyapp.in/v1/conversations/{id}/typing \
  -H "Authorization: Bearer $LAZYAPP_API_KEY"
```

### Archive and pin

Workspace-level flags (API key has no user). Console per-agent pins stay separate.

```bash theme={null}
curl -X PATCH https://lazyapp.in/v1/conversations/{id} \
  -H "Authorization: Bearer $LAZYAPP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"archived": true, "pinned": true}'
```

**Required scope:** `messages:read`

### Assign, resolve and tag

Hand a conversation to the right person from code: a bot that triages a refund request can assign it, mark it pending and tag it in one call.

```bash theme={null}
curl -X PATCH https://lazyapp.in/v1/conversations/{id} \
  -H "Authorization: Bearer $LAZYAPP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"assignee_id": 42, "status": "pending", "tags": ["refund", "priority"]}'
```

`assignee_id` is a member of your team (`null` unassigns). `status` is `open`, `pending`, `resolved` or `closed`. `tags` replaces the conversation's tags, creating new names. Assigning starts `human_assignment` workflows and resolving or closing starts `conversation_closed` ones, exactly as in the inbox. If an agent changes the conversation at the same moment you get `409` with `conversation_changed`; fetch it and try again.

**Required scope:** `messages:send`

### Internal notes

Notes are for agents. They show next to the thread in the inbox and are never sent to the contact, so they are where a bot leaves its triage summary.

```bash theme={null}
curl -X POST https://lazyapp.in/v1/conversations/{id}/notes \
  -H "Authorization: Bearer $LAZYAPP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body": "Refund for order #1042. Damaged on arrival, photo attached in the chat."}'
```

`GET /v1/conversations/{id}/notes` lists them, newest first. A note added over the API has `author: null`.

### Fetch messages

Newest first. `limit` 1–200, default 50.

```bash theme={null}
curl "https://lazyapp.in/v1/conversations/cnv_…/messages?limit=50" \
  -H "Authorization: Bearer $LAZYAPP_API_KEY"
```

Each row includes `uuid`, `direction`, `status`, `type`, `body`, `to` / `from`, `provider_message_id`, and `created_at`.

Prefer [webhooks](https://docs.lazyapp.in/webhooks/overview.md) for live traffic. List endpoints are for backfill and reconciliation — see [Tenant inbox](https://docs.lazyapp.in/platform/inbox.md#webhooks-write-your-inbox-not-polling).

### Reply in a conversation

Address the **contact**, not a conversation id:

```bash theme={null}
curl -X POST https://lazyapp.in/v1/messages \
  -H "Authorization: Bearer $LAZYAPP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-1" \
  -d '{
    "type": "text",
    "to": "+919812345670",
    "body": "Yes, that size is in stock.",
    "context": { "message_id": "wamid.HBgMOTE5ODEyMzQ1Njcw" }
  }'
```

`context.message_id` quotes the inbound message in WhatsApp. Outside the [24-hour window](https://docs.lazyapp.in/sending/messages.md#the-24-hour-window), send a [template](https://docs.lazyapp.in/sending/templates.md) instead — LazyApp refuses free-form sends with `customer_service_window_closed`.

Check the window before choosing a type:

```bash theme={null}
curl https://lazyapp.in/v1/contacts/{contact} \
  -H "Authorization: Bearer $LAZYAPP_API_KEY"
```

`service_window.open` / `state` (`open`, `expiring`, `closed`) tells you whether free-form is allowed.

### Attachments

Upload first ([Media](https://docs.lazyapp.in/sending/media.md)), then send:

```json theme={null}
{
  "type": "image",
  "to": "+919812345670",
  "media_id": "…",
  "caption": "Your receipt"
}
```

LazyApp's upload ceiling is **16 MB** per file by default (`MEDIA_MAX_SIZE_KB`). Meta may still refuse a type or size WhatsApp does not accept for that message kind. Do not invent per-type caps beyond what Meta documents for Cloud API.

Audio sends as a normal audio attachment on `POST /v1/messages`. True **voice-note UI** (waveform) is available from the **console inbox** composer; the public JSON body does not accept a `voice` / `voiceNote` flag today. The Meta-compat proxy can pass Meta's `audio.voice` when you use that path.

### Interactive and commerce

Reply buttons, lists, CTA URL, flows, and [catalog / product / carousel](https://docs.lazyapp.in/sending/catalog.md) messages all use `type: interactive` on [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages). They are session messages — they need an open customer-service window.

When a contact submits a cart, you get `message.received` with `type: order` and `content.order`. Product "Message business" referrals carry product metadata on the inbound message — see [Catalog](https://docs.lazyapp.in/sending/catalog.md) and [Event reference](https://docs.lazyapp.in/webhooks/events.md).

### Reactions

```json theme={null}
{
  "type": "reaction",
  "to": "+919812345670",
  "reaction": {
    "message_id": "wamid.…",
    "emoji": "👍"
  }
}
```

Empty `emoji` removes the reaction. Inbound reactions arrive as `reaction.received` (and also as `message.received` with `type: reaction`).

## Console vs API

| Action | Where |
| - | - |
| Shared inbox UI, per-agent pin / mute | LazyApp **console** |
| Assignment, status, tags, notes, workspace archive / pin | Console, or `PATCH /v1/conversations/{id}` and `/notes` |
| Typing indicator while an agent composes | Console (`POST …/inbox/conversations/{id}/typing`) — session auth |
| Programmatic list / send / webhooks | Public `/v1` API |
| Drop-in tenant UI | [Embedded inbox](https://docs.lazyapp.in/platform/embedded-components.md) |

## Webhooks

Subscribe once; do not poll conversations for new DMs.

| Event | When |
| - | - |
| `message.received` | Inbound message (text, media, buttons, orders, …) |
| `message.sent` / `delivered` / `read` / `played` / `failed` | Outbound lifecycle |
| `message.deleted` | Coexistence revoke deleted a stored message |
| `reaction.received` | Emoji added or removed (`emoji` empty on removal) |
| `conversation.started` | First thread for this contact on a connection |

Full catalogue: [Event reference](https://docs.lazyapp.in/webhooks/events.md). Verify signatures: [Verifying webhooks](https://docs.lazyapp.in/webhooks/verifying.md).

Meta does not expose incoming-message edits or recipient-side unsends, so LazyApp does not emit `message.edited` or inbound deletes as first-class edit events.

## Notes

* **24-hour window** — free-form only after the contact's last message; templates reopen the conversation.
* **Templates** — required to start or restart outside the window.
* **Delivery** — follow status on webhooks; message rows also carry `status`.
* **WhatsApp only** — no unified Instagram / Facebook / Telegram inbox API.

## What we skip (Zernio-style)

| Zernio surface | LazyApp |
| - | - |
| `POST /v1/inbox/conversations/{id}/messages` | [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages) to the recipient |
| `accountId` on every send | Workspace from API key; optional `phone_number_id` / `connection_uuid` |
| Unified multi-network inbox (DMs + comments + reviews) | WhatsApp DMs only |
| Public typing / archive / delete-message APIs | Console (typing, archive); delete not on `/v1` |
| `voiceNote: true` on inbox compose | Console voice; public `/v1` audio attachment without voice flag |

## Related

* [Messages](https://docs.lazyapp.in/sending/messages.md) — every send type
* [Tenant inbox](https://docs.lazyapp.in/platform/inbox.md) — multi-tenant webhook architecture
* [Embedded components](https://docs.lazyapp.in/platform/embedded-components.md) — iframe inbox
* [Catalog](https://docs.lazyapp.in/sending/catalog.md) — product and commerce messages
* [Webhooks](https://docs.lazyapp.in/webhooks/overview.md) — delivery and events


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.