Guides
Conversations
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 to authenticate your requests.
- A connector payment
id— the numeric id returned on a submitted payment intent underexecutions[].connectorPaymentIds. Conversation endpoints are keyed by this id.
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.
Getting a conversation
Retrieve the conversation for a payment. Returns an empty messages array if no conversation exists yet:
curl 'https://api.nexpay.com.au/v2/payments/123/conversation' \
-H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
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);
}
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);
{
"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 -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"
]
}'
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);
}
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. |
Attachments
Attachments must be uploaded via POST /documents before being referenced in a message. See Uploading documents.
Checking for unread messages
Check if a payment's conversation has unread messages without fetching the full thread:
curl 'https://api.nexpay.com.au/v2/payments/123/conversation/unread' \
-H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
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);
}
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 '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
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);
}
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 -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 '{}'
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);
}
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';
}
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
isAdminResponsefield indicates whether a message was sent by the Nexpay admin team. - Attachment IDs are UUID v4 references to documents uploaded via the Documents API.
- Conversations are scoped to your tenant — they cannot be accessed across organizations.