> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuco.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Message History

> Read the full conversation with a lead — inbound and outbound messages together, newest first, paginated.

<Note>
  Pair this with [Get call recordings](/api-reference/endpoint/get-recordings) to
  assemble a complete history for a lead: texts here, calls there.
</Note>

## Overview

Returns messages in your workspace, newest first. Filter by `leadId` to read the
**whole conversation with one lead** — inbound replies and outbound sends in the
same list.

## Authentication

Pass your workspace API key as a Bearer token.

```bash theme={null}
Authorization: Bearer tuco_sk_xxxxxxxxxxxxx
```

***

## Making a request

```bash theme={null}
curl "https://app.tuco.ai/api/messages?leadId=6a4ed2d5dec1b2d01a6f7656&limit=50" \
  -H "Authorization: Bearer tuco_your_key"
```

### Query parameters

<ParamField query="leadId" type="string">
  Only messages for this lead — this is how you read a single conversation.
  24-character ObjectId hex.
</ParamField>

<ParamField query="lineId" type="string">
  Only messages sent from this Tuco line. 24-character ObjectId hex.
</ParamField>

<ParamField query="upstreamCorrelationId" type="string">
  Match the `correlationId` returned in a [drip](/api-reference/drip) `202` ack.
  Useful for confirming a queued drip became a real message.
</ParamField>

<ParamField query="page" type="number">
  Page number, 1-based. Default `1`.
</ParamField>

<ParamField query="limit" type="number">
  Messages per page. Default `50`.
</ParamField>

***

## Looking a conversation up by phone number

<Warning>
  This endpoint has **no phone-number filter**. `leadId` is the only way to scope to
  one conversation.
</Warning>

To start from a phone number, resolve the lead first, then pass its id:

```bash theme={null}
# 1. find the lead
curl "https://app.tuco.ai/api/leads?search=%2B13025550123" \
  -H "Authorization: Bearer tuco_your_key"

# 2. read the conversation
curl "https://app.tuco.ai/api/messages?leadId=THE_LEAD_ID" \
  -H "Authorization: Bearer tuco_your_key"
```

If you only need the lead's **replies** and not the full thread,
[Get replies](/api-reference/endpoint/get-replies) accepts `recipientPhone`
directly and skips the lookup.

***

## Response

```json theme={null}
{
  "messages": [
    {
      "_id": "6a4ed2d5dec1b2d01a6f7656",
      "message": "Sounds good — Tuesday works.",
      "status": "delivered",
      "channelType": "imessage",
      "createdAt": "2026-09-17T15:28:41.632Z",
      "fromLine": {
        "_id": "6a4ed2d5dec1b2d01a6f7600",
        "firstName": "Main",
        "lastName": "Line",
        "phone": "+13025550999",
        "email": "line@example.com"
      },
      "recipient": {
        "_id": "6a4ed2d5dec1b2d01a6f7601",
        "firstName": "Dana",
        "lastName": "Reed",
        "phone": "+13025550123",
        "email": "dana@example.com"
      },
      "attachmentDownloadUrls": ["https://app.tuco.ai/api/attachments/crm-media/photo.jpg?u=...&sig=..."],
      "attachmentNames": ["photo.jpg"]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "totalCount": 128,
    "totalPages": 3
  }
}
```

<ResponseField name="messages" type="array">
  Messages, newest first.

  <Expandable title="message fields">
    <ResponseField name="_id" type="string">Message ID.</ResponseField>
    <ResponseField name="message" type="string">Message body. Empty for attachment-only sends.</ResponseField>
    <ResponseField name="status" type="string">Delivery status, e.g. `queued`, `scheduled`, `sent`, `delivered`, `failed`, `cancelled`.</ResponseField>
    <ResponseField name="channelType" type="string">How it was sent — e.g. `imessage`, `sms`, `email`.</ResponseField>
    <ResponseField name="createdAt" type="string">When the message was created.</ResponseField>
    <ResponseField name="fromLine" type="object">The Tuco line it was sent from (`_id`, `firstName`, `lastName`, `phone`, `email`). `null` if the line has since been removed.</ResponseField>
    <ResponseField name="recipient" type="object">The lead (`_id`, `firstName`, `lastName`, `phone`, `email`). `null` if the lead has since been removed.</ResponseField>
    <ResponseField name="attachmentDownloadUrls" type="array">Signed, fetchable URLs for the message's attachments — **present only when the message has attachments**, max 5.</ResponseField>
    <ResponseField name="attachmentNames" type="array">Filenames matching `attachmentDownloadUrls`. Present only when the message has attachments.</ResponseField>
  </Expandable>
</ResponseField>

<Note>
  The **full stored message document** is returned, so you will see fields beyond those
  listed above — e.g. `attachmentUrls`, `campaignId`, `correlationId`. The fields listed
  are the ones this endpoint guarantees or adds.
</Note>

<ResponseField name="pagination" type="object">
  <Expandable title="pagination fields">
    <ResponseField name="page" type="number">Current page.</ResponseField>
    <ResponseField name="limit" type="number">Page size used.</ResponseField>
    <ResponseField name="totalCount" type="number">Total matching messages.</ResponseField>
    <ResponseField name="totalPages" type="number">Total pages available.</ResponseField>
  </Expandable>
</ResponseField>

<Warning>
  A message's raw `attachmentUrls` point at private storage and return **403** to an API
  key. Use `attachmentDownloadUrls` instead — those are signed and fetchable.
</Warning>

<Note>
  You only ever see messages in your own workspace. The API key you authenticate with
  determines the workspace; there is no parameter to query another one.
</Note>

***

## Errors

| HTTP | Meaning                          |
| ---- | -------------------------------- |
| 401  | Missing or invalid API key       |
| 400  | No active workspace for this key |
| 500  | Unexpected server error          |
