GET
/
api
/
v1
/
chat
/
conversations
/
:id
/
messages
List messages (history)
curl --request GET \
  --url https://api.example.com/api/v1/chat/conversations/:id/messages
import requests

url = "https://api.example.com/api/v1/chat/conversations/:id/messages"

response = requests.get(url)

print(response.text)
const options = {method: 'GET'};

fetch('https://api.example.com/api/v1/chat/conversations/:id/messages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v1/chat/conversations/:id/messages",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.example.com/api/v1/chat/conversations/:id/messages"

req, _ := http.NewRequest("GET", url, nil)

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.example.com/api/v1/chat/conversations/:id/messages")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.example.com/api/v1/chat/conversations/:id/messages")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body

Overview

Returns messages for a conversation, newest first (createdAt desc). Pagination is cursor-based on createdAt: pass the oldest createdAt you’ve already seen as ?before=... to fetch the next older page. This is the standard “infinite scroll up” mobile pattern. The endpoint reuses the participant gate from Get conversation, so the same 403 FORBIDDEN / 404 NOT_FOUND semantics apply.

Authentication

Bearer <accessToken> required. requireOnboarded middleware applied.

Path parameters

NameTypeRequiredNotesExample
idstringyes24-char hex ObjectId of the conversation.66400a8f1c2b4d5e6f7a9000

Query parameters

NameTypeRequiredDefaultNotesExample
beforestringno(none)ISO 8601 UTC datetime. Returns messages with createdAt < before. Use the oldest createdAt from the previous page.2026-05-22T14:35:12.001Z
limitintegerno501..200 inclusive. The client typically fetches 50 per page.50

Request body

None.

Response — 200 OK

FieldTypeNotesExample
messagesobject[]Array of message rows sorted by createdAt desc. Empty array ([]) when the cursor has reached the start of history.

Message object

FieldTypeAllowed values / NotesExample
idstring24-char hex ObjectId. Use as upToMessageId for mark-read.66400a8f1c2b4d5e6f7aa000
conversationIdstring24-char hex ObjectId. Always matches the path :id.66400a8f1c2b4d5e6f7a9000
senderIdstring24-char hex ObjectId of the sender. One of the conversation’s participantIds.66400a8f1c2b4d5e6f7a8b01
clientMessageIdstring1..64 chars. The token the sender’s client used to dedup at send time. Useful for matching local optimistic UI rows to server rows.cm_01HZQ7K3M4N5P6Q7R8S9T0V1W2
messageTypeenumtext, image, video, or file.image
textstringBody for text messages (non-empty); caption for media (may be empty). 0..4000 chars.Hi! Loved your kitchen.
mediaobject | nullPresent iff messageType is image/video/file; null for text. Fields: url, thumbnailUrl, fileName, mimeType, sizeBytes, width, height, durationSec (see send-message → Media object).
deliveredToobject[]One Receipt per recipient that has received the message. Phase 4 returns [] — delivery receipts are wired up but not yet emitted by the worker.[]
readByobject[]One Receipt per recipient that has marked the message read. Excludes the sender (you don’t “read” your own messages).[]
editedAtstring | nullISO 8601 UTC if this message has been edited. null in Phase 4 — edit is not exposed yet.null
deletedAtstring | nullISO 8601 UTC if this message has been soft-deleted. null in Phase 4 — delete is not exposed yet.null
createdAtstringISO 8601 UTC with millisecond precision. Used as the pagination cursor.2026-05-22T14:35:12.001Z
updatedAtstringISO 8601 UTC. Equal to createdAt until edits/receipts mutate the row.2026-05-22T14:35:12.001Z

Receipt object

FieldTypeNotesExample
userIdstring24-char hex ObjectId of the recipient that issued the receipt.66400a8f1c2b4d5e6f7a8b00
atstringISO 8601 UTC when the receipt was recorded server-side.2026-05-22T14:35:18.420Z

Example response

{
  "messages": [
    {
      "id": "66400a8f1c2b4d5e6f7aa001",
      "conversationId": "66400a8f1c2b4d5e6f7a9000",
      "senderId": "66400a8f1c2b4d5e6f7a8b00",
      "clientMessageId": "cm_01HZQ7K3M4N5P6Q7R8S9T0V1W3",
      "text": "Whereabouts in Camden are you?",
      "deliveredTo": [],
      "readBy": [
        { "userId": "66400a8f1c2b4d5e6f7a8b01", "at": "2026-05-22T14:36:01.022Z" }
      ],
      "editedAt": null,
      "deletedAt": null,
      "createdAt": "2026-05-22T14:35:45.612Z",
      "updatedAt": "2026-05-22T14:36:01.022Z"
    },
    {
      "id": "66400a8f1c2b4d5e6f7aa000",
      "conversationId": "66400a8f1c2b4d5e6f7a9000",
      "senderId": "66400a8f1c2b4d5e6f7a8b01",
      "clientMessageId": "cm_01HZQ7K3M4N5P6Q7R8S9T0V1W2",
      "text": "Hi! Loved your kitchen.",
      "deliveredTo": [],
      "readBy": [
        { "userId": "66400a8f1c2b4d5e6f7a8b00", "at": "2026-05-22T14:35:18.420Z" }
      ],
      "editedAt": null,
      "deletedAt": null,
      "createdAt": "2026-05-22T14:35:12.001Z",
      "updatedAt": "2026-05-22T14:35:18.420Z"
    }
  ]
}

Paging sequence

GET /chat/conversations/<id>/messages
→ 50 newest messages
GET /chat/conversations/<id>/messages?before=<oldest createdAt from prev page>
→ next 50 older messages
...
→ eventually `messages: []` (start of history)
The cursor is exclusive: createdAt < before, never <=. Use the previous page’s oldest createdAt verbatim — you will not get duplicate rows.

Error responses

StatusCodeMeaning
400VALIDATION_FAILEDbefore is not an ISO 8601 datetime, or limit is outside 1..200.
401UNAUTHENTICATEDMissing, malformed, or expired access token.
403ONBOARDING_INCOMPLETECaller has not finished onboarding.
403FORBIDDENCaller is not a participant of this conversation.
404NOT_FOUNDNo conversation with that id.

Side effects

None — pure read. Reading a message does not add a readBy receipt; that requires an explicit mark-read call.

See also

curl

# first page
curl -X GET "https://api.swappr.co.uk/api/v1/chat/conversations/66400a8f1c2b4d5e6f7a9000/messages?limit=50" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# older page (use oldest createdAt from previous response)
curl -X GET "https://api.swappr.co.uk/api/v1/chat/conversations/66400a8f1c2b4d5e6f7a9000/messages?before=2026-05-22T14:35:12.001Z&limit=50" \
  -H "Authorization: Bearer $ACCESS_TOKEN"