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

# Calls, texts and contacts

> Read your account's recent calls and texts, everyone you've been in touch with, and your whole history with one person.

These read-only endpoints return what your workspace has said and heard: calls (with transcripts, tags and audio
recordings), texts, emails, and the people on the other end. **Reads are free**, never billed.

| Endpoint | Returns | Scope |
| - | - | - |
| `GET /v1/calls` | Your most recent calls, or one person's, with transcripts, summaries and tags | `calls:read` |
| `GET /v1/calls/{id}` | One call, by id | `calls:read` |
| `GET /v1/calls/{id}/recording` | The call's audio file | `calls:read` |
| `GET /v1/messages` | Your most recent texts, sent and received, or one person's thread | `sms:read` |
| `GET /v1/emails` | Every email with one person, including Quick Emails | `email:read` |
| `GET /v1/contacts` | Everyone you've called, texted or emailed, most recently in touch first | `contacts:read` |
| `GET /v1/contacts/{id}` | One contact's full details: name, phone, email, company, notes, consent | `contacts:read` |

The same reads are available to agents as MCP tools: `list_calls`, `get_call`, `get_call_recording`,
`list_messages`, `list_emails`, `list_contacts` and `get_contact` (see [MCP server](/api-reference/mcp)).

## Recent activity

Leave out the person to get the whole account, newest first:

```bash theme={null}
# The last 20 calls, inbound and outbound, AI and human
curl "https://api.lumisreach.com/api/v1/calls" \
  -H "Authorization: Bearer YOUR_API_KEY"

# The last 20 texts, sent and received
curl "https://api.lumisreach.com/api/v1/messages" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Both take the same filters:

| Parameter | Meaning |
| - | - |
| `direction` | `inbound` (they reached you) or `outbound` (you reached them) |
| `created_after` / `created_before` | Only items created in this window (ISO 8601), e.g. everything since your last sync |
| `contact_id` or `phone_number` | Only one person's calls or texts (see below) |
| `status` (calls only) | `completed`, `voicemail`, `failed`, `busy`, `canceled`, `in_progress`, `ringing`, `queued` |

```bash theme={null}
# Inbound calls since this morning that went to voicemail
curl "https://api.lumisreach.com/api/v1/calls?direction=inbound&status=voicemail&created_after=2026-09-30T00:00:00Z" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Each text carries a `direction` and a `sender`:

| `sender` | Who wrote it |
| - | - |
| `contact` | The person. |
| `ai_agent` | Your AI agent: inbox auto-replies, SMS campaign agents, Quick AI Text threads. |
| `business` | Someone at your business: dashboard inbox, the API, workflows, single quick texts. |

Texts on the shared LumisReach lines that Quick Agent Actions send from are included, so replies to a Quick Text
show up as `inbound` too.

## Everyone you've been in touch with

```bash theme={null}
curl "https://api.lumisreach.com/api/v1/contacts?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "contact_id": "5f25da85-…",
      "name": "Jane Doe",
      "phone_number": "+14155550123",
      "email": "jane@example.com",
      "call_count": 2,
      "text_count": 14,
      "email_count": 1,
      "first_contacted_at": "2026-09-02T16:11:40.000Z",
      "last_contacted_at": "2026-09-25T17:04:12.102Z"
    }
  ],
  "meta": { "cursor": "…", "page_size": 20 }
}
```

`contact_id` is `null` for someone you reached who has no contact record yet.

### A contact's full record

```bash theme={null}
curl "https://api.lumisreach.com/api/v1/contacts/5f25da85-…" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json theme={null}
{
  "data": {
    "id": "5f25da85-…",
    "name": "Jane Doe",
    "first_name": "Jane",
    "last_name": "Doe",
    "phone_number": "+14155550123",
    "email": "jane@example.com",
    "company": "Doe Dental",
    "job_title": "Office Manager",
    "industry": null,
    "location": "San Francisco, CA",
    "preferred_language": "en",
    "date_of_birth": null,
    "notes": "Prefers texts after 5pm.",
    "tags": ["vip"],
    "subscription_plan": null,
    "purchase_history": null,
    "product_preferences": null,
    "custom_fields": { "source": "website" },
    "consent": {
      "sms_opted_in": true,
      "voice_opted_in": false,
      "email_opted_in": false,
      "email_subscribed": true
    },
    "first_inbound_call_at": "2026-09-02T16:11:40.000Z",
    "first_inbound_text_at": null,
    "first_inbound_email_at": null,
    "crm": null,
    "created_at": "2026-09-02T16:11:41.000Z",
    "updated_at": "2026-09-25T17:04:12.381Z"
  }
}
```

The contact id also arrives on every [inbound SMS webhook](/api-reference/sms/webhooks) as
`data.contact.id`. `crm` is set when the contact is synced with a CRM, and holds the provider and
its record id.

## Your history with one person

Pass **one** of `contact_id` or their address: `phone_number` (E.164) for calls and texts, `email` for email. Email
always needs one.

```bash theme={null}
curl "https://api.lumisreach.com/api/v1/messages?phone_number=%2B14155550123" \
  -H "Authorization: Bearer YOUR_API_KEY"

curl "https://api.lumisreach.com/api/v1/emails?contact_id=5f25da85-…" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Emails carry a `source` of `inbox` or `quick` (sent with Quick Email).

## Call details: transcripts, tags and recordings

Each call from `GET /v1/calls` (or one call from `GET /v1/calls/{id}`) carries:

| Field | Meaning |
| - | - |
| `summary` | The AI's summary of the call. |
| `transcript` | The full transcript as plain text. |
| `transcript_turns` | The same, split into turns labelled `agent` or `person`, with `seconds_from_start` when known. |
| `tags` | Labels on the call, the same ones your dashboard shows as badges (below). |
| `has_recording` | Whether there is audio for this call. |
| `recording_url` | Where to download the audio, or `null` when there is none. |

```json theme={null}
{
  "id": "3b0c1f8e-…",
  "direction": "inbound",
  "status": "COMPLETED",
  "from_number": "+14155550123",
  "to_number": "+14157279482",
  "duration_seconds": 94,
  "summary": "Jane asked to move her cleaning to Thursday afternoon.",
  "transcript": "Thanks for calling Doe Dental, how can I help? …",
  "transcript_turns": [
    {
      "speaker": "agent",
      "text": "Thanks for calling Doe Dental, how can I help?",
      "seconds_from_start": 0.4
    },
    { "speaker": "person", "text": "Hi, can I move my cleaning to Thursday?", "seconds_from_start": 3.1 }
  ],
  "tags": ["booking_requested", "qualified"],
  "has_recording": true,
  "recording_url": "https://api.lumisreach.com/api/v1/calls/3b0c1f8e-…/recording",
  "created_at": "2026-09-25T17:02:37.000Z"
}
```

### Tags

| Tag | Meaning |
| - | - |
| `booking_requested` | The person asked to book an appointment. |
| `callback_requested` | The person asked to be called back. |
| `complaint` | The person made a complaint. |
| `purchase_interest` | The person showed interest in buying. |
| `interested` | The AI judged the person interested overall. |
| `qualified` | The call produced a qualified lead. |
| `not_interested` | The person said they are not interested. |
| `transferred` | The AI transferred the call to a person on your team. |

Tags come from the AI's review after the call ends, so a call that just finished may have none for
a few seconds.

### Download the recording

`recording_url` needs the same API key as every other request:

```bash theme={null}
curl "https://api.lumisreach.com/api/v1/calls/3b0c1f8e-…/recording" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o call.mp3
```

The response is the raw audio (usually `audio/mpeg`), never cached. It returns `404` when the call
has no recording, when recording was turned off for it by your compliance settings, or when the call
is not in your workspace. Every download is written to your compliance audit log, attributed to the
API key's owner.

## Pagination

Results come newest first. Pass `meta.cursor` from one response as `cursor` on the next request;
there are no more pages when `meta.cursor` is absent. `limit` defaults to 20 and caps at 100 (50 for
calls, since each one carries a full transcript).

## Keeping up to date

To sync, poll `GET /v1/calls` or `GET /v1/messages` with `created_after` set to the newest `created_at` you have
seen. To hear about new activity the moment it happens instead, register an
[inbound SMS webhook](/api-reference/sms/webhooks) (or open the [text stream](/api-reference/sms/stream)) and a
[call webhook](/api-reference/calls/webhooks), which sends each call's full transcript as soon as it ends.
