Skip to content

Conversations

A conversation is the 1:1 thread between two users. There is at most one per pair of users.

  • 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 policy of your settings has a row from the user’s role to the peer’s role with start_conversation on.
  • The organising features need their own workspace key, which is off by default: aliases, pins, queue and notes. A request that uses a key that is off answers 403 policy_denied with the key as reason.
Terminal window
export MC=https://app.mondochat.io/v1
export SESSION=<session token>
  1. Open a conversation with POST /v1/conversations. with is 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.created webhook. Opening checks bans, then your policy and the safety state. If your policy sets require_vouch for the pair, send a vouch token as well; see Embed.

  2. 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 Conversation a create would return. Nothing is stored: no conversation, no event, no webhook. Its id is 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.

  3. List the inbox with GET /v1/conversations. Pass limit (at most 50) and the next_cursor of the last page as cursor.

    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’s alias, pinned, archived, waiting_since and spent. A conversation with no message yet is not listed. Pinned conversations come first, then the rest by last_message_at, newest first. The inbox holds at most 500 conversations. Keep the latest_seq of the first page: it is the cursor for sync and recovery.

  4. Read one conversation with GET /v1/conversations/{id}. The can flags (message, call, photos, voice, block, buy, offer, reason) come from your policy and the safety state, so a client never has to guess. can.reason is inbound when the peer takes messages from members only and the viewer is a guest.

  5. Turn on workspace.queue to get inbox views. Pass view as to_answer, follow_up or archived. all is 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}. A view of any other value answers 422.

  6. Search with q (1 to 100 characters after trimming). It needs workspace.queue and 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. q with a view other than all answers 422.

    Terminal window
    curl "$MC/conversations?q=invoice" -H "Authorization: Bearer $SESSION"
  7. 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
    alias workspace.aliases Trimmed, at most 64 characters. null or empty clears it.
    pinned workspace.pins At most 10 pinned conversations. The 11th answers 403 limit_reached with reason pins.
    archived workspace.queue A message that is not silently dropped, from either user, unarchives the conversation for both.
    note workspace.notes Trimmed, at most 200 characters, no line break. null or 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.updated to the viewer.

  8. Let a seller invite a guest to sign up with POST /v1/conversations/{id}/invite. It needs workspace.queue and guests.nudge.signup_url. It posts a signup_invite card to the guest and answers the Message. Conversation.invitable tells a client when the call is allowed.

  9. Let sellers keep saved replies with GET and PATCH /v1/me/replies. quick is a list of up to 20 replies and needs workspace.quick_replies. away is {enabled, text} and needs workspace.away_reply, which in turn needs presence.enabled. Only users whose role has seller: true may call it. Sending a quick reply is an ordinary message send.

  1. Open a conversation with the same with twice. The first answer is 201 and the second is 200 with the same id.
  2. Send one message, then list the inbox as either user. The conversation is listed with that message as its preview.
  3. Send PATCH with {"pinned": true} as one user. The conversation comes first in that user’s inbox and the peer’s inbox is unchanged.
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.
  • 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_day in your limits counts 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.