POST
/api/public/v1/conversations/{conversationId}/messages

Send a message

Posts into a conversation, or into a thread with `parentMessageId`. The effects are worth knowing before you automate this. Mentioning somebody who is not in the channel **adds them to it** and texts them a deep link to where they were mentioned. The conversation moves to the top of everyone’s list and every member’s client is notified in real time. A first message in a pending DM makes that conversation visible to its other members. None of that is optional — it is what makes a message a message rather than a row. Write `<@employeeId>` to mention. A message must carry text, at least one attachment, or a poll; an empty one is rejected. You have to be a member to post, even in a public channel.

Required scope

The access token must carry this scope. A token missing it gets 403 Forbidden. Grant them to an API client under Settings → Company → API, and to a teammate under Employee → Permissions.

threads:write

Path Parameters

NameTypeRequiredDescription
conversationIdstringRequiredConversation ID.

Request Body

PropertyTypeDescription
contentstringUp to 8000 characters. May be empty when sending an attachment or a poll. Mention with `<@employeeId>`.
parentMessageIdstringReply inside that message’s thread. Must be a top-level message here.
attachmentsarrayUp to 10 `{ kind, url }` objects — `kind` is FILE or IMAGE — optionally with `storagePath`, `filename`, `mimeType`, `sizeBytes`, `width`, `height`. Upload the file first; this records it.
pollobject`{ question, options }` with 2–10 options, optionally `allowMultiple` and `allowOtherAnswers`. The question becomes the message body so previews and search still work.
POSTExample request
curl
curl -X POST "https://www.pxb.app/api/public/v1/conversations/{conversationId}/messages" \
  -H "Authorization: Bearer your_access_token" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Deploying the billing export now — <@emp_44f1> heads up."
  }'
200Example response
json
{
  "message": "Message sent successfully!",
  "content": {
    "message": {
      "id": "msg_9931",
      "conversationId": "cnv_5150",
      "authorEmployeeId": "emp_12ab",
      "parentMessageId": null,
      "content": "Deploying the billing export now — <@emp_44f1> heads up.",
      "type": "TEXT",
      "replyCount": 2,
      "lastReplyAt": "2026-08-29T18:22:00.000Z",
      "editedAt": null,
      "deletedAt": null,
      "pinnedAt": null,
      "attachments": [],
      "reactions": [],
      "mentions": [
        {
          "employeeId": "emp_44f1"
        }
      ],
      "createdAt": "2026-08-29T18:10:00.000Z"
    }
  }
}
403Example response
json
{
  "message": "Join this channel to send messages."
}
400Example response
json
{
  "message": "This channel is archived."
}

In the console

The same thing done by hand, for anyone comparing the API against the screen they already use.

Threads
/threads
  1. Open a conversation.
  2. Write in the composer and send.