# Inbox

> Read the Inbox, mark conversations as needing no reply, dismiss rows, and answer people who are not leads yet.

The Inbox API returns one ordered list of everyone waiting for an answer, your team chats and what arrived for you, and lets you clear rows and answer someone new before deciding whether they are a lead.

These endpoints take a signed-in member's session token (`Authorization: Bearer <session-token>`); API keys do not open them. See [Authentication](https://www.fondaro.com/docs/api/authentication.md).

Base URL: `https://api.fondaro.com`. Every member can read their own Inbox. Writes (marking handled, dismissing, every `POST` and `DELETE` under `/new-conversations`) need an active plan.

## Read the Inbox

`GET /inbox`

Returns one page of the Inbox, in order, for the person asking.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `view` | Query | `waiting` \| `new` \| `all` | No | The tab. Without it you get the list without team chats, `views` or `note`. |
| `scope` | Query | `me` \| `members` \| `agency` | No | Default `me`. Members always get `me`; `members` and `agency` are for admins. |
| `assigneeIds` | Query | string, repeatable | No | User ids for `scope=members`. Up to 100. |
| `channel` | Query | `email` \| `whatsapp` \| `linkedin` \| `instagram` \| `telegram` | No | Only rows on this channel. |
| `via` | Query | connected account id \| `team` \| `agencies` | No | Only rows from one inbox, your colleagues' chats, or other agencies. |
| `q` | Query | string | No | Rows whose name contains it. With a `view`, also matches `preview`. Up to 200 characters. |
| `cursor` | Query | string | No | `nextCursor` from the previous page. A cursor from another view starts from the top. |
| `limit` | Query | integer | No | 1 to 50. Default 30. |
| `timeZone` | Query | IANA name | No | Used for "tomorrow" and "overnight". Default `UTC`. |

```bash
curl "https://api.fondaro.com/inbox?view=waiting&limit=30&timeZone=Europe/Madrid" \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

```json
{
  "asOf": "2026-09-25T10:00:00.000Z",
  "scope": "me",
  "items": [
    {
      "id": "message:6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11",
      "kind": "awaiting_reply",
      "group": "people",
      "ref": { "kind": "lead", "id": "1042" },
      "conversationKey": null,
      "messageRef": { "kind": "message", "id": "6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11" },
      "connectedAccountId": "3f0c6f8e-1c1a-4a7e-9e7b-2d0c7f6a1b11",
      "occurredAt": "2026-09-25T08:58:12.000Z",
      "lead": { "id": 1042, "firstName": "Lead", "lastName": "Example", "language": "en", "assigneeIds": ["user_2abc"] },
      "title": "Lead Example",
      "channel": "whatsapp",
      "reason": { "code": "wrote", "channel": "whatsapp" },
      "replyOwed": true,
      "automatic": false,
      "fromAnAgency": false,
      "handleable": true,
      "additionalCount": 0,
      "offers": [],
      "preview": "Hi! Is Saturday 11 still ok?",
      "chat": null,
      "faceUrl": "/inbox/faces/message/6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11",
      "unread": true
    }
  ],
  "nextCursor": null,
  "groups": { "reconnect": 0, "people": 1, "team": 0, "invites": 0, "brochures": 0, "network": 0, "posts": 0 },
  "counts": { "awaiting_reply": 1, "new_lead": 0 },
  "waitingCount": 1,
  "hasConnectedAccount": true,
  "view": "waiting",
  "views": { "waiting": 1, "new": 0 },
  "note": null
}
```

### The item object

| Field | Type | Description |
|---|---|---|
| `id` | string | The row's id. Used as `:ref` to dismiss a row with no message. |
| `kind` | string | What the row is (see [Row kinds](#row-kinds)). |
| `group` | string | `reconnect`, `people`, `team`, `invites`, `brochures`, `network` or `posts`. |
| `ref` | object or null | What the row opens: `{ kind, id }` for a lead, `calendar_event`, `brochure`, `listing`, `organization`, `open_house` or chat `conversation`. `null` for `new_conversation` (use `conversationKey`) and `reconnect` (use `connectedAccountId`). |
| `conversationKey` | string or null | For `new_conversation`: the key for the [new conversation endpoints](#answer-someone-new). |
| `messageRef` | object or null | The message the row is about: `{ kind: "email" \| "message", id }`. |
| `connectedAccountId` | string or null | The inbox a message arrived on, or the inbox that needs reconnecting. |
| `occurredAt` | string | ISO 8601. |
| `lead` | object or null | `id`, `firstName`, `lastName`, `language`, `assigneeIds`. |
| `title` | string | The person, agency or chat name. |
| `channel` | string or null | `email`, `whatsapp`, `linkedin`, `instagram` or `telegram`. |
| `reason` | object | Why the row is here, as `{ code, ...parameters }` for your client to put into words. |
| `replyOwed` | boolean | The person is waiting for an answer. |
| `automatic` | boolean | An automatic message or a row a trigger created. |
| `fromAnAgency` | boolean | It came from another agency. |
| `handleable` | boolean | With a `view`: the row can be marked handled or dismissed. |
| `additionalCount` | integer | Other reasons this lead is here. A lead appears once, with its strongest reason. |
| `offers` | object[] | Ask Fondaro's suggestions for the message. Accepting one opens a proposal for the person to approve. |
| `preview` | string or null | The person's own words, about 140 characters at most. `null` when there are none. |
| `chat` | object or null | For team rows: conversation id, `type`, `scope` (`internal` or `external`), `waits`, `unreadCount`, `mentionCount`, `person`, `last`, `mentionedBy`. |
| `faceUrl` | string or null | Path of the person's profile picture (see [Get a profile picture](#get-a-profile-picture)). |
| `unread` | boolean | Something new since you last opened this conversation. Yours alone. |

`groups`, `counts`, `waitingCount` and `views` cover the whole Inbox, not the page and not the `q`, `channel` or `via` filter. `note` (first page of a view only) holds one or two server-chosen sentences as structured parts; it is `null` when there is nothing to say.

### Row kinds

| `kind` | A row when | It leaves when |
|---|---|---|
| `awaiting_reply` | The newest message on a lead, on any channel, is theirs | Anyone replies, a call connects, or it is marked handled |
| `new_conversation` | Someone who is not a lead wrote to one of your inboxes | Add as lead, link, Not a lead, or 14 days |
| `new_lead` | A lead assigned to you that nobody has contacted | Any call, sent email or message |
| `unassigned_lead` | A new lead with nobody on it (admins, `scope=agency`) | Someone is assigned |
| `invite_declined` | A lead declined or answered maybe to your upcoming meeting | The meeting moves, is cancelled or accepted |
| `brochure_opened` | Someone outside your agency opened a brochure you made for one lead | Any contact after the view, or 48 hours |
| `reconnect` | One of your inboxes needs reconnecting | It is connected again |
| `connect_request` | A member asked for an inbox the plan does not include (admins) | Allowed or declined |
| `connect_allowed` | An admin allowed the inbox you asked for | It connects |
| `co_listing_invitation` | Another agency invited you to co-list a home | Accepted or declined |
| `partnership_request` | Another agency asked to partner (admins) | Accepted or declined |
| `open_house_tomorrow` | Your open house is tomorrow and agents said they are going | The day passes |
| `agent_request` | An agent at another agency asked to message you | Accepted or declined |
| `request_reply` | Someone answered your buyer request in a chat | You reply, or the request closes |
| `post_draft` | A listing post waits for you: a draft, a failed post, or one whose account needs reconnecting | Published or discarded |
| `team_dm` | A direct message whose newest message is theirs and unread (with a `view`) | You read or reply |
| `team_mention` | An unread message in a channel or group mentions you (with a `view`) | You read it |
| `team_channel` | A channel or group that does not wait on you (`view=all` only) | Never waits |
| `lead_thread` | A lead's conversation that waits on nobody (`view=all` only) | Never waits |

You only see chats you are a member of, at every scope; an admin's scope changes which leads show, never whose chats.

### Views and order

| `view` | What it holds |
|---|---|
| `waiting` | Every row that waits, waiting chats included |
| `new` | `new_lead`, `new_conversation`, `unassigned_lead` and `agent_request` |
| `all` | Every conversation, newest activity first, with requests to you pinned above the first page |

In `waiting` and `new`, rows come in this order: a `reconnect` that blocks a reply, the people waiting (longest first), the team, declined invites, opened brochures, network rows, then post drafts. Ask Fondaro may lift or sink a row inside its group but never removes one. Paging is by position, so a row that clears between pages never shifts the next one.

## Count who is waiting

`GET /inbox/count`

Returns the number on the Inbox icon: people waiting for you, one per lead, plus chats that owe you an answer and requests to you, always in your own scope.

```bash
curl "https://api.fondaro.com/inbox/count" \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

```json
{ "waitingCount": 3 }
```

## Mark a conversation handled

`POST /inbox/items/:ref/handled`

Marks a message as needing no reply, or dismisses a row that has no message. `DELETE` on the same path undoes it.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `ref` | Path | string | Yes | URL-encoded. A message: `email:<uuid>`, `message:<uuid>`, or `lead:<id>` for the lead's newest message (which must be theirs). A row with no message: the row's `id`. |
| `timeZone` | Query | IANA name | No | Finds an open house "tomorrow" where you are. Default `UTC`. |

A handled message holds on every device; the lead's next message is a new row. A dismissal holds for your whole organization until something newer happens on the row (another brochure view, a new answer to the invite), and you can only dismiss a row your Inbox shows you now. New conversations are decided with the [new conversation endpoints](#answer-someone-new), and chats clear when you read them.

```bash
curl -X POST "https://api.fondaro.com/inbox/items/message%3A6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11/handled" \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

```json
{
  "messageRef": "message:6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11",
  "handled": true,
  "handledAt": "2026-09-25T10:02:41.000Z"
}
```

Marking an already marked message keeps the first mark. `DELETE` returns `"handled": false`.

## Mark many rows at once

`POST /inbox/handled`

Marks, dismisses or undoes up to 100 rows in one request, each by the rules above.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `refs` | Body | string[] | Yes | 1 to 100 refs, each what `:ref` takes above. |
| `handled` | Body | boolean | Yes | `true` to mark or dismiss, `false` to undo. |
| `timeZone` | Body | IANA name | No | Default `UTC`. |

A ref that no longer resolves is listed in `failed`; the rest still land.

```bash
curl -X POST "https://api.fondaro.com/inbox/handled" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"refs": ["message:6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11", "new_lead:lead:1042"], "handled": true}'
```

```json
{
  "results": [
    { "messageRef": "message:6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11", "handled": true, "handledAt": "2026-09-25T10:02:41.000Z" },
    { "messageRef": "new_lead:lead:1042", "handled": true, "handledAt": "2026-09-25T10:02:41.000Z" }
  ],
  "failed": []
}
```

## Mark a conversation read

`POST /inbox/read`

Clears the row's `unread` dot for you alone. It does not clear a waiting row: that still waits until you answer or mark it handled. Needs no active plan.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `key` | Body | string | Yes | `lead:<id>` for any row of a lead, `conversation:<conversationKey>` for a new conversation, the row's `id` for anything else. Chats are read by reading them, not here. |
| `timeZone` | Body | IANA name | No | Default `UTC`. |

```bash
curl -X POST "https://api.fondaro.com/inbox/read" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "lead:1042"}'
```

```json
{ "key": "lead:1042", "readAt": "2026-09-25T10:02:41.000Z" }
```

## Get a profile picture

`GET /inbox/faces/:kind/:id`

Returns the person's WhatsApp, Instagram, LinkedIn or Telegram profile picture as an image. Use the path in an item's `faceUrl` with your session token.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `kind` | Path | `message` \| `conversation` | Yes | A lead's chat message id, or a new conversation's key. |
| `id` | Path | string | Yes | The message id or conversation key. |

```bash
curl "https://api.fondaro.com/inbox/faces/message/6b0c9d0e-5d7a-4e0a-9d59-0f3c1e2a7b11" \
  -H "Authorization: Bearer $SESSION_TOKEN" -o face.jpg
```

The image is cached privately for a day with an `ETag`. It answers `404` when the person has no picture or you cannot see the row.

## Answer someone new

A `new_conversation` row is someone who wrote to one of your inboxes and is not a lead yet. Only the person who owns that inbox, or was given it by an admin, can open and answer it. `:key` is the row's `conversationKey`, URL-encoded. A conversation nobody decides on is deleted after 14 days (`expiresAt`).

| Method and path | Purpose |
|---|---|
| `GET /new-conversations` | Your new conversations (`?channel=` narrows to one channel) |
| `GET /new-conversations/:key` | One conversation with its `messages` |
| `GET /new-conversations/:key/candidates` | Leads it might belong to, with `best` when one is a strong match |
| `POST /new-conversations/:key/reply` | Reply from the inbox they wrote to |
| `POST /new-conversations/:key/add-as-lead` | The lead details, prefilled from the messages. Writes nothing |
| `POST /new-conversations/:key/add-as-lead/confirm` | Create the lead and move the messages onto it |
| `POST /new-conversations/:key/link` | Move the messages onto an existing lead |
| `DELETE /new-conversations/:key` | Not a lead: the stored messages are deleted |

### Reply

`POST /new-conversations/:key/reply`

Sends from the inbox they wrote to. An email goes out as a reply to their newest message, so it stays in their thread; a chat message goes into their chat. The row stays until you decide, with `replyOwed: false`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `body` | Body | string | Yes | Plain text, 1 to 20,000 characters. A chat message is at most 4,000. |
| `subject` | Body | string | No | Email only. Default: `Re:` and their newest subject. |
| `cc` | Body | string[] | No | Email only. Up to 10 addresses. |
| `bcc` | Body | string[] | No | Email only. Up to 10 addresses. |
| `bodyHtml` | Body | string | No | Email only. Formatted body, at most 100,000 characters, cleaned to basic text tags and http, https or mailto links before sending. |

```bash
curl -X POST "https://api.fondaro.com/new-conversations/email.3f0c6f8e-1c1a-4a7e-9e7b-2d0c7f6a1b11.YnV5ZXJAZXhhbXBsZS5jb20/reply" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body": "Hello, the villa is free to view on Saturday at 11."}'
```

```json
{
  "message": {
    "id": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
    "kind": "email",
    "direction": "outbound",
    "subject": "Re: Villa in Mijas",
    "body": "Hello, the villa is free to view on Saturday at 11.",
    "messageAt": "2026-09-25T10:04:00.000Z",
    "attachments": []
  }
}
```

### Add as a lead

`POST /new-conversations/:key/add-as-lead/confirm`

Creates the lead (or matches an existing one with the same details) and moves the conversation onto it. Get prefilled values first with `POST /new-conversations/:key/add-as-lead`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `firstName` | Body | string | Yes | Up to 100 characters. |
| `lastName` | Body | string | No | Up to 100 characters. |
| `email` | Body | string | No | |
| `phoneNumber` | Body | string | No | Up to 50 characters. |
| `language` | Body | string | No | Language code. |
| `propertyListingId` | Body | uuid | No | One of your listings the person asked about. |
| `crmStatus`, `leadType`, `companyName` | Body | string | No | As on lead create. |
| `assigneeIds`, `teamIds`, `unassigned` | Body | | No | Who owns the lead, as on lead create. |

```bash
curl -X POST "https://api.fondaro.com/new-conversations/email.3f0c6f8e-1c1a-4a7e-9e7b-2d0c7f6a1b11.YnV5ZXJAZXhhbXBsZS5jb20/add-as-lead/confirm" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"firstName": "Maria", "lastName": "Lopez", "email": "buyer@example.com"}'
```

```json
{ "leadId": 1077, "created": true, "attachedEmails": 2, "attachedMessages": 0 }
```

`POST /new-conversations/:key/link` takes `{ "leadId": 1042 }` and answers the same shape with `created: false`. `DELETE /new-conversations/:key` answers `{ "deletedEmails": 2, "deletedMessages": 0 }`.

## Live updates

When a message reaches a lead, a new conversation arrives or a row is marked, dismissed or brought back, the dashboard's chat socket sends `inbox:changed` (empty payload) to your organization; after `POST /inbox/read` it goes to your own sockets only. Refetch `GET /inbox` on it, and also every 60 seconds to cover a dropped socket.

## From an MCP client

The [MCP server](https://www.fondaro.com/docs/api/mcp.md) tool `list_inbox` (scope `crm:read`) returns the Inbox as a read-only list. It never returns team chats, and there is no MCP tool to mark a row handled.

## Errors

| Status | Code | When |
|---|---|---|
| `400` | | Invalid parameters, a `:ref` or `key` that is not a message, lead or dismissable row, an empty reply, or a chat reply over 4,000 characters |
| `403` | `SUBSCRIPTION_REQUIRED` | A write without an active plan |
| `403` | `SANDBOX_OUTBOUND_DISABLED` | A reply from a sandbox organization |
| `404` | | No such message, row, lead or conversation that you can see |
| `409` | `mailbox_needs_reconnect`, `message_needs_reconnect`, `mailbox_not_connected`, `message_not_connected`, `message_account_restricted` | The inbox cannot send. `integration` names the setup page |
| `409` | | Linking a sender who is already linked to another lead |
| `429` | `message_paced`, `message_rate_limited`, `mailbox_rate_limited` | Sending is paused; try again later |
| `502` | `mailbox_send_failed`, `message_send_failed` | The provider did not take the reply; try again |

Errors use the standard body: `{ "statusCode", "message", "error" }`, plus `code` where listed. Authenticated `GET` requests share a limit of 120 requests per endpoint per 10 seconds per person; a `429` carries `Retry-After`.

Source: https://www.fondaro.com/docs/api/inbox
