> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.fortresstech.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.fortresstech.io/_mcp/server.

# List Messages

GET https://api.fortresstech.io/v1/messages

Available by RequestList messages, narrowed by conversation or by one of the identifiers assigned when the message was
created. Supply exactly one of `conversationId`, `idempotencyId`, or `messageSid`.Use `idempotencyId` to confirm whether a send you issued was recorded, and `messageSid` to resolve a
carrier identifier from a delivery receipt back to the Fortress message. To read a message whose
Fortress `id` you already hold, use `GET /messages/{messageId}` instead.This endpoint is part of our evolving API ecosystem.
To explore availability for your integration, please contact our team.Filtering, sorting, field selection, and pagination are not available on this endpoint. The `fields`, `sort`, `filter`, `page`, and `limit` query parameters are ignored.

Reference: https://developer.fortresstech.io/api-reference/messaging/list-messages

## Authentication

- `x-api-key` header (required) — API Key authentication via header

## Request

### Query parameters

- `conversationId` (string, optional) — Return every message on this conversation, oldest first.
- `idempotencyId` (string, optional) — Return the message created with this idempotency key.
- `messageSid` (string, optional) — Return the messages matching this carrier-assigned identifier.

## Response

### 200

Expected response to a valid request. Returns an empty array when nothing matches.

- `list of MessageRead`

## Errors

### 400 Bad Request Error

No identifier was supplied, or more than one was supplied.

- `type` (string, optional)
- `title` (string, optional)
- `status` (string, optional)
- `detail` (string, optional)
- `instance` (string, optional)

## Types

### MessageRead

- `isReaction` (boolean, required) — True when an inbound message is a phone tapback reaction (for example `Loved "See you at 3"`) rather than a written message (same rule as `message.received` `isReaction`). Always present on `GET /messages` and `GET /messages/{messageId}`; false for outbound messages.
- `id` (string, optional) — Unique identifier for the message.
- `conversationId` (string, optional) — The conversation this message belongs to. Messages to the same recipient from the same property are grouped into a single conversation.
- `type` (enum, optional) — Direction and origin of the message. Messages created through `POST /messages` are always `OUTBOUND`.
  - Allowed values: `OUTBOUND`, `INBOUND`, `INTERNAL`, `EMAIL`, `UNRECOGNIZED_KEYWORD_RESPONSE`
- `content` (string, optional) — The body of the message. Limited to 1600 characters on send.
- `messageStatus` (string, optional) — Current delivery status. Statuses reported by the downstream carrier or email provider are passed through verbatim, so this is an open set rather than a fixed enum. Statuses set by Fortress: `CREATED`, `PROCESSING`, `FAILED`, `INVALID_PHONE_NUMBER`, `INVALID_EMAIL`, `RECEIVED`, `AUTOMATED`, `MASS_COMMUNICATION`. Statuses reported by the carrier: `QUEUED`, `SENT`, `DELIVERED`, `UNDELIVERED`, `FAILED`. The terminal statuses for SMS are `DELIVERED`, `UNDELIVERED`, and `FAILED`. For email they are `DELIVERED`, `FAILED`, and `REJECTED`.
- `messageStatusReason` (string, optional, nullable) — Explanation for the current status. Populated when a message fails to send.
- `messageSid` (string, optional) — Identifier assigned by the downstream SMS carrier. Absent until the message has been accepted for delivery.
- `customerStatus` (string, optional) — Status of the recipient at the time the message was created, as recorded in Fortress.
- `idempotencyId` (string, optional) — Caller-supplied key that uniquely identifies the send request. Reusing a key returns the original message rather than sending a duplicate.
- `isAutomated` (boolean, optional) — True when the message was generated by a Fortress automation rather than a person.
- `needsHelp` (boolean, optional, nullable) — True while this message has an open needs-help flag. Absent on messages that have never been flagged.
- `needsHelpDetails` (MessageReadNeedsHelpDetails, optional, nullable) — Who raised the needs-help flag, why, and who resolved it. Absent on messages that have never been flagged. The flag details survive resolution, so this still describes the original request after `needsHelp` returns to false.
- `subject` (string, optional) — Subject line. Email messages only.
- `emailAddress` (string, optional) — Recipient email address. Email messages only.
- `attachments` (list of MessageReadAttachmentsItems, optional) — Media attached to the message.
- `metadata` (map from string to any, optional) — Caller-supplied metadata, plus `sentAt` and `sentBy` recorded by Fortress at send time.
- `organizationId` (string, optional)
- `customerId` (string, optional) — Set when the recipient is a customer.
- `prospectId` (string, optional) — Set when the recipient is a prospect.
- `userId` (string, optional) — The Fortress user the message was sent on behalf of.
- `createdAt` (datetime, optional)
- `updatedAt` (datetime, optional)

### MessageReadNeedsHelpDetails

Who raised the needs-help flag, why, and who resolved it. Absent on messages that have never been flagged. The flag details survive resolution, so this still describes the original request after `needsHelp` returns to false.

- `reason` (string, optional, nullable) — Free-text reason supplied when the flag was raised.
- `flaggedByType` (enum, optional, nullable) — Who raised the flag: `agent` for Leslie, `user` for a Fortress staff member.
  - Allowed values: `agent`, `user`
- `flaggedById` (string, optional, nullable) — Identifies who raised the flag. `leslie` for the agent, otherwise a Fortress user id.
- `flaggedAt` (datetime, optional, nullable) — When the flag was raised.
- `resolvedByType` (enum, optional, nullable) — Who resolved the flag, using the same `agent`/`user` distinction as the flag itself.
  - Allowed values: `agent`, `user`
- `resolvedById` (string, optional, nullable) — Identifies who resolved the flag.
- `resolvedAt` (datetime, optional, nullable) — When the flag was resolved. Null while the flag is still open.

### MessageReadAttachmentsItems

- `key` (string, optional)
- `bucket` (string, optional)
- `type` (string, optional) — MIME type of the attachment.
- `url` (string, optional) — Time-limited download URL, generated per request. Expires 15 minutes after the response is issued.

## Examples

**Response**

```json
[
  {
    "isReaction": true,
    "id": "string",
    "conversationId": "string",
    "type": "OUTBOUND",
    "content": "string",
    "messageStatus": "string",
    "messageStatusReason": "string",
    "messageSid": "string",
    "customerStatus": "string",
    "idempotencyId": "string",
    "isAutomated": true,
    "needsHelp": true,
    "needsHelpDetails": {
      "reason": "string",
      "flaggedByType": "agent",
      "flaggedById": "string",
      "flaggedAt": "2024-01-15T09:30:00Z",
      "resolvedByType": "agent",
      "resolvedById": "string",
      "resolvedAt": "2024-01-15T09:30:00Z"
    },
    "subject": "string",
    "emailAddress": "string",
    "attachments": [
      {
        "key": "string",
        "bucket": "string",
        "type": "string",
        "url": "string"
      }
    ],
    "metadata": {},
    "organizationId": "string",
    "customerId": "string",
    "prospectId": "string",
    "userId": "string",
    "createdAt": "2024-01-15T09:30:00Z",
    "updatedAt": "2024-01-15T09:30:00Z"
  }
]
```

**SDK Code**

```python
import requests

url = "https://api.fortresstech.io/v1/messages"

headers = {"x-api-key": "<apiKey>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.fortresstech.io/v1/messages';
const options = {method: 'GET', headers: {'x-api-key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api.fortresstech.io/v1/messages"

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

	req.Header.Add("x-api-key", "<apiKey>")

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

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

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.fortresstech.io/v1/messages")

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

request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<apiKey>'

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

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.fortresstech.io/v1/messages")
  .header("x-api-key", "<apiKey>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.fortresstech.io/v1/messages', [
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.fortresstech.io/v1/messages");
var request = new RestRequest(Method.GET);
request.AddHeader("x-api-key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["x-api-key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fortresstech.io/v1/messages")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```