Conversations
A conversation is the 1:1 thread between two users. There is at most one per pair of users.
Before you start
Section titled “Before you start”- Both users exist. A user exists after their first session, or after you create them from your server. Opening a conversation with an unknown external id answers 404. See Sessions and tokens and Users.
- You have a session token for the user who opens the conversation. Every route here is a user route:
Authorization: Bearer <session token>. - The
policyof your settings has a row from the user’s role to the peer’s role withstart_conversationon. - The organising features need their own
workspacekey, which is off by default:aliases,pins,queueandnotes. A request that uses a key that is off answers 403policy_deniedwith the key asreason.
export MC=https://app.mondochat.io/v1export SESSION=<session token>-
Open a conversation with
POST /v1/conversations.withis the peer’s external id.Terminal window curl -X POST $MC/conversations -H "Authorization: Bearer $SESSION" \-H 'content-type: application/json' -d '{"with": "bob"}'const mc = (path, init = {}) =>fetch(`https://app.mondochat.io/v1${path}`, {...init,headers: { authorization: `Bearer ${process.env.SESSION}`, 'content-type': 'application/json', ...init.headers },})const res = await mc('/conversations', { method: 'POST', body: JSON.stringify({ with: 'bob' }) })const conversation = await res.json()The answer is 201 for a new conversation and 200 for the existing one. A new conversation sends the
conversation.createdwebhook. Opening checks bans, then your policy and the safety state. If your policy setsrequire_vouchfor the pair, send avouchtoken as well; see Embed. -
Check whether the user may open it before you store anything. Send
"draft": true.Terminal window curl -X POST $MC/conversations -H "Authorization: Bearer $SESSION" \-H 'content-type: application/json' -d '{"with": "bob", "draft": true}'When the pair has no conversation yet, the answer is 202 with the
Conversationa create would return. Nothing is stored: no conversation, no event, no webhook. Itsidis unknown to every other route and answers 404. When the pair already has one, the answer is 200 with that conversation. Every refusal a create gives, a draft gives too. -
List the inbox with
GET /v1/conversations. Passlimit(at most 50) and thenext_cursorof the last page ascursor.Terminal window curl "$MC/conversations?limit=50" -H "Authorization: Bearer $SESSION"const inbox = await (await mc('/conversations?limit=50')).json()The answer is
{items, next_cursor, latest_seq, removed, next, has_more, counts}. Each item has the peer, a preview of the last message,unread_count, and the viewer’salias,pinned,archived,waiting_sinceandspent. A conversation with no message yet is not listed. Pinned conversations come first, then the rest bylast_message_at, newest first. The inbox holds at most 500 conversations. Keep thelatest_seqof the first page: it is the cursor for sync and recovery. -
Read one conversation with
GET /v1/conversations/{id}. Thecanflags (message,call,photos,voice,block,buy,offer,reason) come from your policy and the safety state, so a client never has to guess.can.reasonisinboundwhen the peer takes messages from members only and the viewer is a guest. -
Turn on
workspace.queueto get inbox views. Passviewasto_answer,follow_uporarchived.allis the default and leaves archived conversations out.to_answer: conversations the peer is waiting on. Pinned first, then regulars (the peer has a paid purchase in it), then the rest by the peer’s tier and last message.follow_up: conversations where the viewer wrote last and got no reply for at least 24 hours. Pinned first, then those whose last message is the viewer’s offer card, then regulars, then the rest.archived: archived conversations, pinned first.
The first page of every view carries
counts: {to_answer, follow_up}. Aviewof any other value answers 422. -
Search with
q(1 to 100 characters after trimming). It needsworkspace.queueand matches the peer’s name, the viewer’s alias, the viewer’s note, and the text or caption of messages the viewer can see. Archived conversations are included. A view-once caption never matches.qwith aviewother thanallanswers 422.Terminal window curl "$MC/conversations?q=invoice" -H "Authorization: Bearer $SESSION" -
Organise a conversation with
PATCH /v1/conversations/{id}. Every field is private to the viewer, and fields you leave out stay as they are.Terminal window curl -X PATCH $MC/conversations/$ID -H "Authorization: Bearer $SESSION" \-H 'content-type: application/json' \-d '{"alias": "Anna (regular)", "pinned": true, "archived": false, "note": "Prefers evenings"}'Field Needs Rules aliasworkspace.aliasesTrimmed, at most 64 characters. nullor empty clears it.pinnedworkspace.pinsAt most 10 pinned conversations. The 11th answers 403 limit_reachedwith reasonpins.archivedworkspace.queueA message that is not silently dropped, from either user, unarchives the conversation for both. noteworkspace.notesTrimmed, at most 200 characters, no line break. nullor empty clears it.The fields apply together or not at all. The change goes to the viewer only: the peer gets nothing, and neither do webhooks, your server API or moderators. A change publishes
conversation.updatedto the viewer. -
Let a seller invite a guest to sign up with
POST /v1/conversations/{id}/invite. It needsworkspace.queueandguests.nudge.signup_url. It posts asignup_invitecard to the guest and answers theMessage.Conversation.invitabletells a client when the call is allowed. -
Let sellers keep saved replies with
GETandPATCH /v1/me/replies.quickis a list of up to 20 replies and needsworkspace.quick_replies.awayis{enabled, text}and needsworkspace.away_reply, which in turn needspresence.enabled. Only users whose role hasseller: truemay call it. Sending a quick reply is an ordinary message send.
Check it works
Section titled “Check it works”- Open a conversation with the same
withtwice. The first answer is 201 and the second is 200 with the sameid. - Send one message, then list the inbox as either user. The conversation is listed with that message as its preview.
- Send
PATCHwith{"pinned": true}as one user. The conversation comes first in that user’s inbox and the peer’s inbox is unchanged.
When it fails
Section titled “When it fails”| Answer | Cause |
|---|---|
| 404 | The peer was never created, or the viewer is not a participant. |
| 422 | The user opened a conversation with themselves, or a field is out of range, or q is blank or longer than 100 characters. |
403 banned |
The user or their IP is banned. See Reports, blocks and bans. |
403 policy_denied |
Your policy refuses it. The reason names the rule: require_vouch, queue, aliases, pins, notes, invite, seller, quick_replies or away_reply. |
403 limit_reached |
The user is past new_conversations_per_day, has 10 pins, or sent a second invite within 24 hours. |
410 gone |
A changed_since cursor is too old. Reload the inbox and sync from the new latest_seq. |
Limits
Section titled “Limits”- 50 conversations per page and 500 in the inbox. The first page always holds every pinned conversation.
- Only conversations with a message are listed, for both users.
- At most 10 pins, 64 characters for an alias, 200 for a note, 20 quick replies.
new_conversations_per_dayin yourlimitscounts only stored conversations, never drafts.- When a guest signs up and merges into a member, the peer’s alias, pin and note carry over to the surviving conversation.
Related topics
Section titled “Related topics”- Open a conversation, list conversations, get a conversation, patch a conversation, invite a guest
- Quick and away replies
- Settings reference for
workspace,limitsandpolicy - Events and sync and recovery
- Errors