/api/public/v1/conversations/{conversationId}/messagesSend 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.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
conversationId | string | Required | Conversation ID. |
Request Body
| Property | Type | Description |
|---|---|---|
content | string | Up to 8000 characters. May be empty when sending an attachment or a poll. Mention with `<@employeeId>`. |
parentMessageId | string | Reply inside that message’s thread. Must be a top-level message here. |
attachments | array | Up 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. |
poll | object | `{ question, options }` with 2–10 options, optionally `allowMultiple` and `allowOtherAnswers`. The question becomes the message body so previews and search still work. |
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."
}'{
"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"
}
}
}{
"message": "Join this channel to send messages."
}{
"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.