# listContactTextThreads

Every text thread ONE contact has, across the org lines the key owner may see. A slim index: ids and phone numbers, NO message bodies: each entry is { id (the conversationId), contactE164PhoneNumber, phoneLineId, twilioPhoneNumberE164 (the line that carries it) }. Its job is to disambiguate before you read: a contact texted on two different lines has two threads, and getTextThreadMessages needs to be told which. Unpaginated: a contact has a handful of threads, not a feed. An EMPTY LIST means this contact has no text threads on any line you can see, which is a real answer about the contact. It is NOT how a foreign contact reads: a contactId outside your organization is REFUSED (403 forbidden), never answered with an empty list, so you can always tell "no texts" from "not your contact". Bodies come from getTextThreadMessages, or from listContactActivities with activityTypes: [TEXT] when you want texts inline with the rest of the timeline.

- Call: `POST https://server.goliathdata.com/api/v1/call` with `{ "operationId": "listContactTextThreads", "variables": {...} }`
- Scope: `READ`
- Type: query
- Idempotency: a read; rejects `Idempotency-Key`
- Rate-limited: no

## Variables

- `contactId`: `ID!` (required)
- `userId`: `ID`

## Response (data, an example)

```json
{
  "inboxQuery": {
    "getContactTextThreads": [
      {
        "id": "<ID>",
        "contactE164PhoneNumber": "…",
        "phoneLineId": "<ID>",
        "twilioPhoneNumberE164": "…"
      }
    ]
  }
}
```

## Example

```bash
curl https://server.goliathdata.com/api/v1/call \
  -H "Authorization: Bearer $GOLIATH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operationId": "listContactTextThreads",
    "variables": {
      "contactId": "<contactId>"
    }
  }'
```
