Connected inboxes
Connect, reconnect and disconnect your inboxes, choose their calendars, and email or message leads from them.
A connected inbox (in the API, a connected account) is a mailbox, WhatsApp, Instagram, LinkedIn or Telegram account a member connected to Fondaro. Use these endpoints to manage inboxes and to email or message a lead from them; every email and message goes out from the person's own account.
These endpoints take a signed-in member's session token (Authorization: Bearer <session-token>); API keys do not open them. See Authentication.
Base URL: https://api.fondaro.com. Each member connects at most one inbox of each kind. Connecting is a browser round trip: you get a link, the person signs in with the provider, and Fondaro never receives a password.
The connected account object
| Field | Type | Description |
|---|---|---|
id | uuid | |
kind | string | mailbox, shared_mailbox, whatsapp, instagram, linkedin or telegram |
provider | string | google, microsoft, imap (other mailbox), or the chat channel's own name |
address | string or null | The email address, number or handle, once known |
status | string | connecting, ok, needs_reconnect, error or disconnected |
statusReason | string or null | Why the status last changed |
statusChangedAt | string | ISO 8601 |
capabilities | string[] | What works now: email, calendar, messaging |
connectedAt | string or null | ISO 8601 |
disconnectedAt | string or null | Always null in lists |
ownerUserId | string | The member who connected it |
assignedUserIds | string[] | Other people an admin gave this inbox to |
historyMode | string | from_now (the default) or last_90_days. See Choose whether past messages come in |
calendars | object[] | Only with the calendar capability. See Choose which calendars show |
needs_reconnect means the provider stopped accepting the connection (a changed password, or access removed). Reconnecting restores it with the same id.
List your inboxes
GET /connected-accounts
Returns your live inboxes and the colleagues' inboxes an admin gave you. available is false when connecting is not available; the lists are then empty.
List the organization's inboxes
GET /connected-accounts/organization
Admins only. Returns every member's live inboxes as an array. Each item adds owner (name, email, imageUrl) and included (true when it is one of the plan's included inboxes, the oldest ones; false when it is one of the extra inboxes bought).
Check the price of the next inbox
GET /connected-accounts/price
Returns whether the next inbox fits in your organization's inboxes, how many are free, the price of one extra inbox, and whether you may add one to the plan yourself.
| Field | Description |
|---|---|
included | true while fewer inboxes are connected than your organization has (included plus bought), so anyone on the team can connect the next one |
room | How many inboxes are still free. Absent for an organization with no limit |
amountCents, currency | The monthly price of one extra inbox, in minor units |
canAddPaid | true for an admin, who can add an inbox to the plan (Change your inboxes); false for a member, who asks an admin |
requests | Your own open requests (see Ask for an inbox), pending or allowed |
GET /billing/plan-usage returns the plan's people and inbox totals:
included is one inbox per member plus any bonus; capacity is included + bought; room is capacity - inUse, never below 0. monthlyBought is null for a member.
Change your inboxes
Admins only. Each extra inbox is a monthly charge on your plan, like a person. inboxes is always the total after the change (included plus bought).
GET /billing/inboxes/preview?inboxes=7 previews the change without making it:
PATCH /billing/inboxes makes it. Send a fresh requestId per change and the same one when you retry it, so a retry never adds twice.
Adding inboxes is charged now for the rest of the billing period, and lands only once that payment succeeds (402 PAYMENT_ACTION_REQUIRED otherwise). Removing inboxes credits the unused days on the next invoice. During a trial nothing is charged until it ends.
| Status | Code | When |
|---|---|---|
400 | below_included | Fewer than the inboxes your plan includes |
409 | inboxes_in_use | Fewer than the inboxes connected now. The body lists the newest ones in connected (id, ownerName, kind, address): disconnect one first |
409 | BILLING_STATE_CONFLICT | No active plan, or an organization with no limit |
402 | PAYMENT_ACTION_REQUIRED | The card was not charged; nothing changed |
Connect an inbox
POST /connected-accounts/connect
Starts a connection and returns a url to open in the person's browser. They return to the matching Integrations page with ?connected=<provider> on success or ?connect_error=<reason> on failure.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
provider | Body | string | Yes | google, microsoft, imap, whatsapp, instagram, linkedin or telegram |
kind | Body | string | No | Defaults to mailbox for an email provider, else the provider. shared_mailbox (the agency's shared inbox) is for admins only |
historyMode | Body | string | No | from_now (default) or last_90_days. Stored on the inbox when it lands. Stored as from_now when the agency has turned past messages off |
The inbox appears in GET /connected-accounts once the provider confirms it; poll until status is ok.
When every inbox is in use, connecting is refused with the facts in the body (capacity, inUse, pricePerInbox): an admin gets 409 inboxes_full (add an inbox first with PATCH /billing/inboxes), a member 403 admin_approval_required (ask for one). Connecting itself never charges anything.
Choose whether past messages come in
PATCH /connected-accounts/:id
Each inbox has a history choice. from_now brings in only new mail and messages: no import of the past when it connects, and no pull of a chat's past when a lead first writes. last_90_days imports the last 90 days of mail with people who are already your leads when it connects, and pulls the last 90 days of a chat when a lead first writes. Find past emails and Find past messages are your own request on one lead and work in either case.
Only the person who connected the inbox can change it (for the shared inbox, the admin who connected it). Switching a healthy mailbox to last_90_days starts the mail import once, in the background. Switching to from_now stops later pulls and deletes nothing. Inboxes connected before 29 October 2026 are from_now.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
historyMode | Body | string | Yes | from_now or last_90_days |
Returns the inbox. An admin can turn past messages off for the whole agency with PATCH /organization/inbox-settings and {"inboxHistoryAllowed": false}; every inbox then behaves as from_now, and asking for last_90_days returns 409 history_off.
Share your conversations on a lead
GET /connected-accounts/sharing, PATCH /connected-accounts/sharing
One switch per person, off until you turn it on. With it on, a colleague assigned to the same lead as you sees your emails and messages with that lead on the lead's page in Fondaro, read-only. They can't see your inbox, reply from it, or open its files, and your conversations never appear in their Inbox. It covers every inbox you connected except the shared inbox. Only your own switch is read or changed here; nobody else's, an admin's included.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
shareWithLeadColleagues | Body | boolean | Yes | true to share, false to stop |
Both return { "shareWithLeadColleagues": true, "agencyAllows": true }. An admin can turn sharing off for the whole agency with PATCH /organization/inbox-settings and {"inboxSharingAllowed": false}: nothing is shared then, whatever each person chose, and turning it back on restores each person's own choice. While it is off, turning your switch on returns 409 sharing_off; turning it off always works.
Ask for an inbox
POST /connected-accounts/requests
When every inbox is in use, a member asks the admins instead (body as for connect). While one is free, asking returns 409 not_needed: connect instead. Admins get a notification and see the request on Settings › Me › Inboxes. Asking again for the same kind returns the open request.
Admins list open requests with GET /connected-accounts/requests (each adds userId, requester and the monthly amountCents and currency), and answer with POST /connected-accounts/requests/:id/allow or POST /connected-accounts/requests/:id/decline. Allowing tells the member they can connect. They connect while an inbox is free, so when every inbox is in use, add one with PATCH /billing/inboxes before allowing. Answering a request that is no longer open returns 409 request_closed.
Give an inbox to other people
PUT /connected-accounts/:id/assignees
Admins only. Sets everyone besides the owner who uses this inbox (such as the agency's info@). They see its new conversations and can send from it. Replaces the whole list.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | Path | uuid | Yes | The connected account |
userIds | Body | string[] | Yes | Up to 200 member user ids. [] removes everyone |
Reconnect an inbox
POST /connected-accounts/:id/reconnect
Returns a url, as for connect. Only the member who connected the inbox can reconnect it. It keeps its id and uses no extra inbox.
Disconnect an inbox
DELETE /connected-accounts/:id
Disconnects at once and answers 204 with no body. The owner or any admin can disconnect. Conversations already on leads stay. There is no refund for the current month.
Choose which calendars show
A Google or Microsoft inbox with the calendar capability lists its calendars in calendars. A calendar someone else owns and shared into the inbox is listed only when the inbox's owner is an organization admin; for a member it is left out and its events leave their calendar.
| Field | Type | Description |
|---|---|---|
id | string | The provider's calendar id. URL-encode it in a path |
name | string or null | As named in Google or Outlook |
primary | boolean | The main calendar. It always shows |
readOnly | boolean | Shared for reading only. New events never go to it |
selected | boolean | Its events show on the person's calendar. A new calendar starts hidden, except the primary |
PUT /connected-accounts/:id/calendars/:calendarId with { "selected": true | false } shows or hides one calendar. POST /connected-accounts/:id/calendars/refresh (no body) lists the calendars again, for one made after connecting. Both act on your own inbox only and answer { "calendars": [...] }.
Email a lead
See which address you send from
GET /crm/email-sender
Returns how an email you send now would leave, so a composer can show the From address or a Connect step.
via is mailbox or none (nothing to send from). An inbox that needs reconnecting still answers mailbox with its status. options lists every inbox you may pick as From whose status is ok: your own, then those an admin gave you.
Send an email
POST /crm/leads/:id/emails
Sends from your connected mailbox. It lands in your Sent folder, and the lead's reply comes back to your inbox and onto the lead.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | Path | integer | Yes | The lead |
subject | Body | string | Yes | Up to 500 characters |
bodyText | Body | string | Yes | The plain version, always stored |
bodyHtml | Body | string | No | Formatted body, up to 100,000 characters, cleaned to basic text tags and http, https or mailto links |
fromAccountId | Body | uuid | No | One of your options. Default: your own mailbox |
cc, bcc | Body | string[] | No | Up to 10 addresses each |
signatureHtml | Body | string | No | Your signature, cleaned before sending |
replyToEmailId | Body | uuid | No | An email on the lead's timeline to reply to, so it threads |
documentIds | Body | uuid[] | No | Up to 10 uploaded Documents to attach, 20 MB together |
The response is the email as it now sits on the lead's timeline, with toAddresses, ccAddresses and bccAddresses.
Find past emails
POST /crm/leads/:id/find-past-emails
Searches your connected inboxes for the lead's email address over the last 12 months and adds what it finds to the lead within a minute or two. Use it for a lead created after you connected (connecting brings in the last 90 days once). Safe to repeat. Answers 202.
Message a lead
List the lead's channels
GET /crm/leads/:id/channels
Lists the channels you can message this lead on: you have a live WhatsApp, Instagram, LinkedIn or Telegram connection and the lead is reachable there (on WhatsApp by their number, elsewhere because they wrote to you or a member linked their profile). An empty list means no message channel.
block says why canSend is false: needs_reconnect or restricted (the provider restricted the account). newChat is true when the next message opens a chat with someone new. manyNewChats is true when it does and the account has already started many new chats in the last 24 hours: advice to spread them out, never a block.
Send a message
POST /crm/leads/:id/messages
Sends from your own connection of that channel. A person sends every message: there is no scheduled, bulk or automated sending.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | Path | integer | Yes | The lead |
channel | Body | string | Yes | whatsapp, instagram, linkedin or telegram |
body | Body | string | Yes | Up to 4,000 characters. May be empty when a file goes alone |
attachments | Body | uuid[] | No | Up to 3 uploaded Documents ids, 10 MB together |
To protect your account, messages from one account go at least 15 seconds apart (Instagram 10 seconds, and at most 10 an hour); a send inside that gap waits up to 30 seconds. A first message to someone new is never held back.
GET /crm/leads/:id/messages/:messageId/attachments/:index returns a short-lived download link (url, filename, contentType) for a stored file on a message.
Link a lead's channel
Link a WhatsApp number, Instagram, LinkedIn or Telegram identity, or an extra email address, to a lead so messages there land on it.
| Method and path | Purpose |
|---|---|
GET /crm/leads/:id/channel-identities | The identities linked to the lead |
POST /crm/leads/:id/channel-identities/lookup | Who that is on the channel, before linking. Writes nothing |
POST /crm/leads/:id/channel-identities | Link it, and attach what they already wrote |
DELETE /crm/leads/:id/channel-identities/:identityId | Unlink it (204) |
POST /crm/leads/:id/find-past-messages | Read the last year of the lead's chats on your own accounts |
Look up a lead's profile
POST /crm/leads/:id/lookup-profile
Reads the lead's linked LinkedIn or Instagram profile once, through your own connection. It changes nothing on the lead. LinkedIn allows about 80 look ups a day per account.
Errors
| Status | Code | When |
|---|---|---|
400 | invalid_kind | The kind cannot use that provider |
400 | not_a_member | An assignee is not in your organization |
400 | sender_not_allowed | fromAccountId is not one of your options |
400 | lead_has_no_email | Find past emails on a lead with no email address |
402 | not_entitled | Connecting needs an active plan or trial |
403 | ORG_ADMIN_REQUIRED | An admin-only action, or the shared mailbox |
403 | admin_approval_required | A member connecting when every inbox is in use. Ask for one |
403 | not_owner | Reconnecting, disconnecting or changing the history of someone else's inbox |
403 | sandbox, SANDBOX_OUTBOUND_DISABLED | Sandbox organizations cannot connect inboxes or send |
403 | mailbox_not_connected, mailbox_needs_reconnect | Sending an email without a working mailbox |
403 | message_not_connected, message_needs_reconnect, message_account_restricted | Sending a message without a working connection |
403 | SUBSCRIPTION_REQUIRED | A lead write without an active plan |
404 | CONNECTED_ACCOUNT_NOT_FOUND, CALENDAR_NOT_FOUND, lookup_not_found | Not found, or not yours |
409 | already_connected | You already have a live inbox of this kind |
409 | inboxes_full | An admin connecting when every inbox is in use. Add one first |
409 | history_off | Asking for last_90_days while your agency has turned past messages off |
409 | sharing_off | Turning sharing on while your agency has turned it off |
409 | not_needed, request_closed | No request is needed, or it is no longer open |
409 | CALENDAR_PRIMARY_LOCKED, CONNECTED_ACCOUNT_NEEDS_RECONNECT | Hiding the primary calendar, or refreshing an inbox that needs reconnecting |
409 | mailbox_not_connected, mailbox_needs_reconnect | Find past emails without a working mailbox |
409 | channel_linked_elsewhere | The identity is linked to another lead |
422 | message_not_on_channel, channel_invalid, lookup_not_linked | The lead is not reachable there, the value is not valid, or no profile is linked |
429 | message_paced, message_rate_limited, mailbox_rate_limited, lookup_limited | Try again after retryAfterSeconds |
502 | message_send_failed, mailbox_send_failed, provider_error | The provider did not take it; try again |
503 | not_available | Connecting is not available |
Errors use the standard body { "statusCode", "message", "error" }, plus code and any fields listed above.