## Search Query Cheat Sheet

| Goal | Query | Notes |
| --- | --- | --- |
| All threads | `Communication?part-of:missing=true` | Returns only thread headers |
| Messages in a thread | `Communication?part-of=Communication/{id}&_sort=sent` | Sorted chronologically |
| Threads for a patient | Add `&subject=Patient/{id}` to any query above | Filter by patient context |
| Threads I participate in | Add `&recipient=Practitioner/{id}` | Header must list the creator in `recipient`; see [note below](/content/docs/communications/searching-and-querying-threads#filter-by-current-user/index.html) and [modeling tip](/content/docs/communications/messaging-data-model#building-and-structuring-threads/index.html) |
| Active threads only | Add `&status:not=completed,entered-in-error,stopped,unknown` | Exclude closed threads |

## Query for All Threads

- Typescript
- CLI
- cURL

```ts
// Search reference: find all thread headers, sorted by most recently active

await medplum.searchResources('Communication', {

'part-of:missing': true,

'status:not': 'completed,entered-in-error,stopped,unknown',

_sort: '-_lastUpdated',

});
```

```bash
medplum get 'Communication?part-of:missing=true&status:not=completed,entered-in-error,stopped,unknown&_sort=-_lastUpdated'
```

```bash
curl 'https://api.medplum.com/fhir/R4/Communication?part-of:missing=true&status:not=completed,entered-in-error,stopped,unknown&_sort=-_lastUpdated' \
  -H 'authorization: Bearer $ACCESS_TOKEN' \
  -H 'content-type: application/fhir+json'
```

The `:missing` modifier finds Communication resources that have no `partOf` reference — these are thread headers. Adding `status:not=completed` filters to only active threads. Sorting by `-_lastUpdated` (descending) puts the most recently active threads first.

## Query for Messages in a Thread

- Typescript
- CLI
- cURL

```ts
// Retrieve all messages in a thread, sorted chronologically

await medplum.searchResources('Communication', {

'part-of': `Communication/${threadHeader.id}`,

_sort: 'sent',

});
```

```bash
medplum get 'Communication?part-of=Communication/{threadHeaderId}&_sort=sent'
```

```bash
curl 'https://api.medplum.com/fhir/R4/Communication?part-of=Communication/{threadHeaderId}&_sort=sent' \
  -H 'authorization: Bearer $ACCESS_TOKEN' \
  -H 'content-type: application/fhir+json'
```

This retrieves all child messages for a given thread header, sorted chronologically by the `sent` timestamp.

## Add Filters

Filter threads by patient:

- Typescript
- CLI
- cURL

```ts
// Filter threads to a specific patient

await medplum.searchResources('Communication', {

'part-of:missing': true,

subject: 'Patient/homer-simpson',

});
```

```bash
medplum get 'Communication?part-of:missing=true&subject=Patient/homer-simpson'
```

```bash
curl 'https://api.medplum.com/fhir/R4/Communication?part-of:missing=true&subject=Patient/homer-simpson' \
  -H 'authorization: Bearer $ACCESS_TOKEN' \
  -H 'content-type: application/fhir+json'
```

### Filter by Current User

To show threads the current user participates in, filter by `recipient`:

- Typescript
- CLI
- cURL

```ts
// Filter to only the current user's active threads

await medplum.searchResources('Communication', {

'part-of:missing': true,

recipient: getReferenceString(profile),

'status:not': 'completed,entered-in-error,stopped,unknown',

});
```

```bash
medplum get 'Communication?part-of:missing=true&recipient=Practitioner/{id}&status:not=completed,entered-in-error,stopped,unknown'
```

```bash
curl 'https://api.medplum.com/fhir/R4/Communication?part-of:missing=true&recipient=Practitioner/{id}&status:not=completed,entered-in-error,stopped,unknown' \
  -H 'authorization: Bearer $ACCESS_TOKEN' \
  -H 'content-type: application/fhir+json'
```

This works when thread headers list all participants — including the thread creator — in `recipient`. The Medplum [`ThreadInbox`](https://github.com/medplum/medplum/blob/main/packages/react/src/chat/ThreadInbox/ThreadInbox.tsx) component follows this convention: when a new thread is created, the sender is added to `recipient` alongside other participants, so a `recipient`-based query returns threads the user started _and_ threads they were added to.

For other items to filter on, see the [Communication Search Parameters](/content/docs/api/fhir/resources/communication#search-parameters/index.html).

### Live Updates

To receive new messages in real time, subscribe to Communications in the current thread. WebSocket subscriptions may need to be enabled for your project (for example, the `websocket-subscriptions` feature). The subscription criteria for a specific thread:

```text
Communication?part-of=Communication/{threadId}
```

When a notification arrives, append the new message to your local state. The notification `Bundle` contains a `SubscriptionStatus` entry and the new `Communication` — find it by `resourceType` rather than by position:

```ts
const threadId = 'example-thread-id';

const emitter = medplum.subscribeToCriteria(`Communication?part-of=Communication/${threadId}`);

emitter.addEventListener('message', (event) => {

const newMessage = event.payload.entry?.find((e) => e.resource?.resourceType === 'Communication')?.resource;

if (newMessage) {

console.log(newMessage);

}

});
```

If using `@medplum/react`, the [useSubscription](/content/docs/react/use-subscription/index.html) hook handles connection management:

```tsx
useSubscription(

`Communication?part-of=Communication/${id}`,

(bundle: Bundle) => {

const newMessage = bundle.entry?.find(

(e) => e.resource?.resourceType === 'Communication'

)?.resource as Communication | undefined;

if (newMessage) {

// Show a toast or in-app notification for the new message

setMessages((prev) => [...prev, newMessage]);

}

}

);
```

See the [useSubscription](/content/docs/react/use-subscription/index.html) documentation for WebSocket connection setup and reconnection handling.
