# Conversations

> Learn how to use payment conversations to exchange messages and attachments.

Conversations allow you to exchange messages within the context of a specific payment. Each payment can have one conversation thread where parties can discuss details, share documents, and resolve queries.

---

## Before you start

You will need:

- A valid [API key](/docs/api-keys.md) to authenticate your requests.
- A connector payment `id` — the numeric id returned on a submitted [payment intent](/docs/guides/payment-intents-manual.md) under `executions[].connectorPaymentIds`. Conversation endpoints are keyed by this id.

> **Note — JSON examples show the unwrapped `data` payload**
>
> Every success response on the wire is `{ "data": { ... } }`. The JSON examples in this guide show the inner `data` value — what you get after `const { data } = await response.json()`. Error responses are **not** wrapped — see [Errors](/docs/errors.md).

## Getting a conversation

Retrieve the conversation for a payment. Returns an empty messages array if no conversation exists yet:

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/payments/123/conversation' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/payments/123/conversation', {
    method: 'GET',
    headers: {
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
  });

  if (response.ok) {
    const { data: conversation } = await response.json();

    console.log(conversation);
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->get('https://api.nexpay.com.au/v2/payments/123/conversation');

$conversation = $response->json('data');

print_r($conversation);
```

```javascript
{
  "id": 42,
  "paymentId": 123,
  "isClosed": false,
  "isRead": true,
  "messages": [
    {
      "userId": 300,
      "isAdminResponse": false,
      "subject": "Payment query",
      "text": "Can you confirm the enrollment letter was received?",
      "createdOn": "2026-03-15T09:00:00.000Z",
      "userFullName": "Jane Doe",
      "attachments": []
    },
    {
      "userId": 1,
      "isAdminResponse": true,
      "text": "Yes, we have received all documents. Payment is being processed.",
      "createdOn": "2026-03-15T10:30:00.000Z",
      "userFullName": "Support Team",
      "attachments": []
    }
  ]
}
```

---

## Sending a message

Send a message in the payment conversation. If no conversation exists, one is created automatically:

**cURL**

```bash
curl -X POST 'https://api.nexpay.com.au/v2/payments/123/conversation/messages' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
  -H 'Content-Type: application/json' \
  -d '{
    "subject": "Payment query",
    "text": "Can you confirm the enrollment letter was received?",
    "attachments": [
      "f47ac10b-58cc-4372-a567-0e02b2c3d479"
    ]
  }'
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/payments/123/conversation/messages', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
    body: JSON.stringify({
      "subject": "Payment query",
      "text": "Can you confirm the enrollment letter was received?",
      "attachments": [
        "f47ac10b-58cc-4372-a567-0e02b2c3d479"
      ]
    }),
  });

  if (response.ok) {
    const { data: message } = await response.json();

    console.log(message);
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->post('https://api.nexpay.com.au/v2/payments/123/conversation/messages', [
    'subject' => 'Payment query',
    'text' => 'Can you confirm the enrollment letter was received?',
    'attachments' => [
        'f47ac10b-58cc-4372-a567-0e02b2c3d479',
    ],
]);

$message = $response->json('data');

print_r($message);
```

### Message fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `text` | string | Yes | The message content. |
| `subject` | string | No | Optional subject line for the message. |
| `attachments` | string[] | No | Array of document UUIDs uploaded via the [Documents API](/docs/guides/uploading-documents.md). |

> **Note — Attachments**
>
> Attachments must be uploaded via `POST /documents` before being referenced in a message. See [Uploading documents](/docs/guides/uploading-documents.md).

---

## Checking for unread messages

Check if a payment's conversation has unread messages without fetching the full thread:

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/payments/123/conversation/unread' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/payments/123/conversation/unread', {
    method: 'GET',
    headers: {
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
  });

  if (response.ok) {
    const { data } = await response.json();

    console.log(data); // { "hasUnread": true }
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->get('https://api.nexpay.com.au/v2/payments/123/conversation/unread');

$data = $response->json('data');

print_r($data); // [ 'hasUnread' => true ]
```

---

## Downloading an attachment

Download a file attached to a conversation message:

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/payments/123/conversation/attachments/f47ac10b-58cc-4372-a567-0e02b2c3d479' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
  --output attachment.bin
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/payments/123/conversation/attachments/f47ac10b-58cc-4372-a567-0e02b2c3d479', {
    method: 'GET',
    headers: {
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
  });

  if (response.ok) {
    const blob = await response.blob();

    console.log('Downloaded attachment:', blob.size, 'bytes');
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->get('https://api.nexpay.com.au/v2/payments/123/conversation/attachments/f47ac10b-58cc-4372-a567-0e02b2c3d479');

$contents = $response->body();

echo 'Downloaded attachment: ' . strlen($contents) . ' bytes';
```

---

## Closing a conversation

Close a conversation when it's no longer needed:

**cURL**

```bash
curl -X POST 'https://api.nexpay.com.au/v2/payments/123/conversation/close' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/payments/123/conversation/close', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
    body: JSON.stringify({}),
  });

  if (response.ok) {
    console.log('Conversation closed');
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->post('https://api.nexpay.com.au/v2/payments/123/conversation/close', []);

if ($response->successful()) {
    echo 'Conversation closed';
}
```

> **Warning — Closed conversations**
>
> Once a conversation is closed, no new messages can be sent. Attempting to send a message to a closed conversation will return a `[CNV0002]` error.

---

## Things to know

* Each payment can have at most one conversation.
* Conversations are created automatically when the first message is sent.
* The `isAdminResponse` field indicates whether a message was sent by the Nexpay admin team.
* Attachment IDs are UUID v4 references to documents uploaded via the [Documents API](/docs/guides/uploading-documents.md).
* Conversations are scoped to your tenant — they cannot be accessed across organizations.
