# Read and send messages

> Read your chats, send messages to your connections, star, pin, mute or archive chats, and run group chats with the Ur Next Door API.

Source: https://docs.urnextdoor.com/guides/messages/

Messages are the most personal thing a key can touch, so the `messages` scope is never ticked by default, and a key may **start** a chat only with one of your connections (`403 dm_not_allowed` otherwise). A chat you already have, started on the website or in the app, continues as usual.

**Scope:** `messages` · **Quota:** `message` for sending, forwarding and groups; reading and chat settings are not counted

## Chats and their keys

Every chat has a key: a group's slug, or `yourname-theirname` for a direct chat (both usernames, joined by a hyphen). `GET /api/v1/chats` lists your chats with their keys; `GET /api/v1/chats/contacts` lists the connections you can start a chat with.

| What | Endpoint |
| --- | --- |
| Your chats | `GET /api/v1/chats` (`filter=archived` for archived ones) |
| One chat, with members and pins | `GET /api/v1/chats/{key}` |
| Its messages | `GET /api/v1/chats/{key}/messages` (`before=` for older, `after=` for newer) |
| Search | `GET /api/v1/chats/search?q=`, or `GET /api/v1/chats/{key}/search?q=` in one chat |
| Unread count | `GET /api/v1/chats/unread-count` |
| Starred messages | `GET /api/v1/chats/starred` |

## Send

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/chats/aarav-asha/messages" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "body": "See you at 6!"
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/chats/aarav-asha/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    body: "See you at 6!",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/chats/aarav-asha/messages",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "body": "See you at 6!",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'body' => 'See you at 6!',
];

$ch = curl_init('https://urnextdoor.com/api/v1/chats/aarav-asha/messages');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

Also: `POST /api/v1/chats/{key}/messages/{messageId}/forward` with `{"to": "<chat key>"}`, and `POST /api/v1/chats/{key}/messages/{messageId}/react` with an `emoji` (the same emoji again removes it).

## Chat settings

`PUT /api/v1/chats/{key}/state` sets any of `archived`, `pinned` and `muted` (with `mute_seconds`). Each is set, never flipped: a repeat changes nothing.

**cURL**

```bash
curl -X PUT "https://urnextdoor.com/api/v1/chats/aarav-asha/state" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "muted": true,
  "mute_seconds": 3600
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/chats/aarav-asha/state", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    muted: true,
    mute_seconds: 3600,
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.put(
    "https://urnextdoor.com/api/v1/chats/aarav-asha/state",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    json={
        "muted": True,
        "mute_seconds": 3600,
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'muted' => true,
    'mute_seconds' => 3600,
];

$ch = curl_init('https://urnextdoor.com/api/v1/chats/aarav-asha/state');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

`PUT /api/v1/chats/messages/{id}/starred` stars a message; `POST /api/v1/chats/{key}/read` marks a chat read; `POST /api/v1/chats/{key}/pins` pins a message.

## Groups

`POST /api/v1/chats/groups` starts a group with your connections (`member_ids`). In a group you run, add a connection (`POST .../members`), set a role (`.../role`, with `role` `admin` or `member` always sent), rename it, set its icon, or leave it. Adding someone already in the group answers `409`, never a silent role change.

## Next steps

- [Follow, connect and block](https://docs.urnextdoor.com/guides/network.md): connections decide who a key can message.
- [Keep your API key safe](https://docs.urnextdoor.com/security.md).
- [API reference: Messages](https://docs.urnextdoor.com/reference/messages.md).
