# Inbox API

One unified view across every connected account: DMs, comments, reviews, mentions and email, plus contacts, labels, broadcasts and inbox analytics.

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

The Inbox API is one view of everything people send your accounts: DMs, comments, reviews, mentions and email, from every channel. Contacts, labels, broadcasts and analytics complete it into a support desk.

## What you can build

- **A support desk or shared inbox.** List open conversations across every account with `GET /v1/inbox/conversations` and answer each from the account that received it.
- **An inbox inside your own product.** Give each customer a profile and read their inbox by `profileId` ([multi-tenant inbox](/multi-tenant/inbox)).
- **Review management.** Find unanswered reviews with `GET /v1/inbox/reviews` and reply with `POST /v1/inbox/reviews/{reviewId}/reply`.
- **A CRM view of your audience.** Keep the people behind conversations as contacts with `GET /v1/contacts`, tag them, and message a segment with `POST /v1/broadcasts`.
- **Response-time reporting.** Read volume and response times with `GET /v1/analytics/inbox/volume` and `GET /v1/analytics/inbox/response-time`.

## Platforms

<ProductPlatforms product="inbox" />

Each platform brings what its API exposes:

| In the inbox | Platforms |
|---|---|
| DMs | Instagram, Facebook, WhatsApp, X, Telegram, Bluesky, Reddit, TikTok, Slack, iMessage, RCS, SMS |
| Comments | Instagram, Facebook, X, Threads, TikTok, YouTube, LinkedIn, Reddit, Bluesky |
| Reviews | Facebook, Google Business Profile |
| Mentions | Instagram, Facebook, LinkedIn organization pages |
| Email | Gmail, Outlook, IMAP mailboxes |

TikTok needs an account connected through TikTok's Business app. The [Messaging API](/products/messaging) and the [Comments API](/products/comments) give the per-platform rules for DMs and comments.

## How it works

The inbox is a set of list endpoints that read every account of a profile in one call, or all of your accounts without `profileId`. Narrow them with `platform` or `accountId`.

| Part | What it is | Start with |
|---|---|---|
| Conversations | DM and email threads, with their messages | [List conversations](/messages/list-inbox-conversations) |
| Comments | Posts with comments, then each thread | [List commented posts](/comments/list-inbox-comments) |
| Reviews | Facebook Page and Google Business Profile reviews | [List reviews](/reviews/list-inbox-reviews) |
| Mentions | Posts and comments that tag your account, last 7 days | [List mentions](/mentions/list-inbox-mentions) |
| Contacts | The people behind conversations, with tags and custom fields | [List contacts](/contacts/list-contacts) |
| Labels | Facebook Page labels on Messenger conversations | [List labels](/messages/list-inbox-labels) |
| Broadcasts | One message sent to many contacts | [Create broadcast](/broadcasts/create-broadcast) |
| Analytics | Volume, response time, sources and busiest hours | [Inbox volume](/inbox-analytics/get-inbox-volume) |

Every item carries its `platform` and `accountId`, so you always answer from the account that received it. Ids are opaque: pass them back as you got them.

New items arrive through the [inbox webhooks](/webhooks/inbox): `message.received`, `comment.received`, `review.new` and `mention.received`. Use the list endpoints to backfill when an account connects, and on a schedule to catch a missed event.

## Your first request

You need an API key and a profile with connected accounts. This lists the active conversations across every account in the profile, most recent first.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: conversations } = await zernio.messages.listInboxConversations({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4', status: 'active', limit: 50 },
});

console.log(conversations.data.length, conversations.pagination.nextCursor);
```
</Tab>
<Tab value="Python">
```python
from zernio import Zernio

client = Zernio()

conversations = client.messages.list_inbox_conversations(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    status="active",
    limit=50,
)

print(len(conversations["data"]), conversations["pagination"]["nextCursor"])
```
</Tab>
<Tab value="curl">
```bash
curl "https://zernio.com/api/v1/inbox/conversations?profileId=66a1f0c2a4b9d3e8f1a2b3c4&status=active&limit=50" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"
```
</Tab>
</Tabs>

Response (`200`):

```json
{
  "data": [
    {
      "id": "66c3d2ae7b4f6c8d0e1f2a3b",
      "platform": "instagram",
      "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
      "participantName": "Jane",
      "lastMessage": "Hi, is this still available?",
      "updatedTime": "2026-11-09T14:00:03Z",
      "status": "active",
      "unreadCount": 2
    },
    {
      "id": "66c3d2ae7b4f6c8d0e1f2a3c",
      "platform": "whatsapp",
      "accountId": "66b2e19d8c3f5a7e9d0b1c2e",
      "participantName": "Marco",
      "lastMessage": "Can I change my order?",
      "updatedTime": "2026-11-09T13:41:10Z",
      "status": "active",
      "unreadCount": 0
    }
  ],
  "pagination": { "hasMore": true, "nextCursor": "eyJ1cGRhdGVkVGltZSI6..." },
  "meta": { "accountsQueried": 3, "accountsFailed": 0, "failedAccounts": [] }
}
```

Read a thread with [Get messages](/messages/get-inbox-conversation-messages) and answer it with [Send message](/messages/send-inbox-message).

## Examples

### Work through unread conversations

`unreadCount` counts the unread incoming messages of each conversation. Keep the ones above zero, answer them, then mark each one read.

<Tabs items={['Node.js', 'Python']}>
<Tab value="Node.js">
```typescript
const { data: page } = await zernio.messages.listInboxConversations({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4', status: 'active', limit: 100 },
});

const unread = page.data.filter((c) => (c.unreadCount ?? 0) > 0);

for (const conversation of unread) {
  await zernio.messages.markConversationRead({
    path: { conversationId: conversation.id },
    body: { accountId: conversation.accountId },
  });
}
```
</Tab>
<Tab value="Python">
```python
page = client.messages.list_inbox_conversations(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    status="active",
    limit=100,
)

unread = [c for c in page["data"] if (c.get("unreadCount") or 0) > 0]

for conversation in unread:
    client.messages.mark_conversation_read(
        conversation_id=conversation["id"],
        account_id=conversation["accountId"],
    )
```
</Tab>
</Tabs>

Follow `pagination.nextCursor` for the next page.

### Show only VIP customers

Contacts carry your own `tags`, across every channel. Pass `tags` to the conversation list to keep the threads whose contact has any of them.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: vip } = await zernio.messages.listInboxConversations({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4', tags: 'vip,wholesale' },
});
```
</Tab>
<Tab value="Python">
```python
vip = client.messages.list_inbox_conversations(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    tags="vip,wholesale",
)
```
</Tab>
<Tab value="curl">
```bash
curl "https://zernio.com/api/v1/inbox/conversations?profileId=66a1f0c2a4b9d3e8f1a2b3c4&tags=vip,wholesale" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"
```
</Tab>
</Tabs>

Set a contact's tags with [Update contact](/contacts/update-contact). On Messenger you can also apply the Page's own labels with [Add conversation label](/messages/add-conversation-label).

### Answer low-rated reviews

`hasReply=false` with `maxRating=3` lists the unanswered reviews of three stars or fewer, across Facebook Pages and Google Business Profile locations.

<Tabs items={['Node.js', 'Python']}>
<Tab value="Node.js">
```typescript
const { data: reviews } = await zernio.reviews.listInboxReviews({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4', hasReply: false, maxRating: 3 },
});

for (const review of reviews.data) {
  await zernio.reviews.replyToInboxReview({
    path: { reviewId: review.id },
    body: {
      accountId: review.accountId,
      message: `Sorry to hear this, ${review.reviewer.name}. Please write to help@example.com so we can fix it.`,
    },
  });
}
```
</Tab>
<Tab value="Python">
```python
reviews = client.reviews.list_inbox_reviews(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    has_reply=False,
    max_rating=3,
)

for review in reviews["data"]:
    client.reviews.reply_to_inbox_review(
        review_id=review["id"],
        account_id=review["accountId"],
        message=f"Sorry to hear this, {review['reviewer']['name']}. Please write to help@example.com so we can fix it.",
    )
```
</Tab>
</Tabs>

On Google Business Profile, a reply replaces any earlier reply to the same review.

## How it behaves

- **Partial results.** An account that fails is skipped and named in `meta.failedAccounts`. The rest still return.
- **Meta history.** When an Instagram or Facebook account connects, up to 500 older conversations are replayed into the inbox in the background, already read and without webhooks.
- **Zero content retention.** With it on, lists return ids and metadata with message text `null` ([zero content retention](/security/zero-content-retention)).
- **Billing.** Reading is free. Sent DMs and broadcasts count toward [outbound messages](/pricing/inbox).

## If it fails

| Error | Cause | Fix |
|---|---|---|
| `403` `INBOX_REQUIRED` | A legacy plan without inbox access | Start the trial or upgrade. Usage-based billing includes the inbox |
| `meta.accountsFailed` above 0 | An account's token stopped working | Reconnect the accounts in `meta.failedAccounts` |
| `400` `platform_not_supported` on labels | Labels exist only on Facebook Messenger | Use contact `tags` on other channels |
| A new DM is missing from the list | The list was read before the message was stored | Read it again, or rely on `message.received` |

## Related

- [Messaging API](/products/messaging)
- [Comments API](/products/comments)
- [Build an inbox for your customers](/multi-tenant/inbox)
- [Inbox webhooks](/webhooks/inbox)
- [Inbox pricing](/pricing/inbox)
- [Automations](/products/automations)