The Contact Inbox
The nine endpoints that read, move and answer the contact form inbox.
Overview
Messages from the contact form behave like an inbox: unread, read, replied, spam and trash. These nine endpoints read that inbox, move messages between folders and carry out two real actions.
The folder is not a stored field; it is computed from the record's status and its read mark. Moving a message to read leaves the status alone and flips the mark instead.
Two endpoints reach outside the installation: replying sends the visitor a real e-mail, and converting opens a real support ticket. Neither can be undone.
Reference
Listing the Messages
Returns the contact form messages in a folder.
unread, read, replied, spam, trash. The unread ones by default.unread, read, replied, spam, trash. Derived from the status and the read mark.curl 'https://panel.example.com/api/v1/admin/website/messages?folder=unread' \
-H "Authorization: Bearer $API_KEY"const url = new URL('https://panel.example.com/api/v1/admin/website/messages');
url.searchParams.set('folder', 'unread');
const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages?' . http_build_query(['folder' => 'unread']));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The read mark is INVERTED: zero means unread and one means read.
$msgs = Api::Website()->GetMessages([], ['folder' => 'unread'])['data'];
$isRead = $msgs[0]['unread'] === 1;Reading One Message
Returns a single message.
unread, read, replied, spam, trash. Derived from the status and the read mark.curl 'https://panel.example.com/api/v1/admin/website/messages/20' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/website/messages/${id}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Reading does NOT mark it read; the mark is set by a call of its own.
$msg = Api::Website()->GetMessage(['id' => $id])['data'];Moving Messages in Bulk
Moves several messages into another folder in one call.
unread, read, replied, spam, trash.curl -X POST 'https://panel.example.com/api/v1/admin/website/messages/bulk-move' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"ids":[20,21],"folder":"trash"}'const res = await fetch('https://panel.example.com/api/v1/admin/website/messages/bulk-move', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids: [20, 21], folder: 'trash' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/bulk-move');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['ids' => [20, 21], 'folder' => 'trash']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Two options reach PAST THE MESSAGE: the sender is banned and their address blocked.
Api::Website()->BulkMoveMessages([
'ids' => $ids,
'folder' => 'spam',
'block_emails' => true,
'report_spam' => true,
]);Emptying a Folder
Empties the spam or the trash folder outright.
curl -X POST 'https://panel.example.com/api/v1/admin/website/messages/empty-folder' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"folder":"trash"}'const res = await fetch('https://panel.example.com/api/v1/admin/website/messages/empty-folder', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ folder: 'trash' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/empty-folder');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['folder' => 'trash']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// It does NOT say how many went and cannot be undone; list the folder and count first.
$before = Api::Website()->GetMessages([], ['folder' => 'trash'])['meta']['total'];
Api::Website()->EmptyMessageFolder(['folder' => 'trash']);Moving One Message
Moves one message into another folder.
unread, read, replied, spam, trash.curl -X PUT 'https://panel.example.com/api/v1/admin/website/messages/20/folder' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"folder":"read"}'const res = await fetch(`https://panel.example.com/api/v1/admin/website/messages/${id}/folder`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ folder: 'read' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/' . $id . '/folder');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['folder' => 'read']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The read and unread folders leave the STATUS alone and flip the read mark instead.
Api::Website()->SetMessageFolder(['id' => $id, 'folder' => 'read']);Marking a Message Read
Marks a message read and records who read it.
curl -X POST 'https://panel.example.com/api/v1/admin/website/messages/20/read' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/website/messages/${id}/read`, {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/' . $id . '/read');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The readers map keeps EACH administrator apart, while the mark is one for the panel.
Api::Website()->ReadMessage(['id' => $id]);Replying to a Message
Sends the visitor an e-mail reply and moves the message to replied.
curl -X POST 'https://panel.example.com/api/v1/admin/website/messages/20/reply' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"message":"Ilginiz icin tesekkurler, en kisa surede donecegiz."}'const res = await fetch(`https://panel.example.com/api/v1/admin/website/messages/${id}/reply`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: "Thanks for reaching out - we'll get back to you shortly.",
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/' . $id . '/reply');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'message' => 'Thanks for reaching out.',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// This call MAILS THE VISITOR and cannot be undone; check the text before sending.
Api::Website()->ReplyMessage([
'id' => $id,
'message' => $text,
]);Turning It into a Ticket
Opens a support ticket from a message and ties the two together.
curl -X POST 'https://panel.example.com/api/v1/admin/website/messages/20/convert-to-ticket' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"department":1,"priority":2}'const res = await fetch(`https://panel.example.com/api/v1/admin/website/messages/${id}/convert-to-ticket`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ department: 1, priority: 2 }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/' . $id . '/convert-to-ticket');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['department' => 1, 'priority' => 2]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The message CARRIES which ticket it became; read that before converting again.
$msg = Api::Website()->GetMessage(['id' => $id])['data'];
if ($msg['converted_to_ticket_id'] === 0)
Api::Website()->ConvertMessageToTicket(['id' => $id, 'department' => 1]);Deleting a Message
Removes one message for good.
curl -X DELETE 'https://panel.example.com/api/v1/admin/website/messages/20' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/website/messages/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/messages/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Move it to TRASH rather than delete: the record stays and can come back if needed.
Api::Website()->SetMessageFolder(['id' => $id, 'folder' => 'trash']);Pitfalls
In the read field zero means unread and one means read. The name suggests the opposite, so the logic here often gets read backwards and the inbox count comes out wrong. Test against one rather than trusting the value as a boolean.
What the record holds is the status and the read mark, and the folder is derived from those two. Moving a message to read leaves the status alone and flips the mark, while moving it to spam or trash changes the status. When filtering on your side, use the computed field rather than the raw status.
Two options on the move calls reach far past the message: one adds the sender's e-mail and phone to the banned list, the other blocks their address. Both apply across the installation and keep that person from using the form again. Leave them off when doing a bulk clean-up.
The reply endpoint saves no draft; it sends the visitor a real e-mail, and a sent message cannot be pulled back. The convert endpoint likewise opens a real ticket. When trying these two from a script, use a test record that reaches your own address.
The empty call works on the spam and trash folders alone, yet there it removes everything for good and does not say how many went. The response names only the folder emptied. When you need a record, list the folder before emptying it.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.