Error Logs
The seven endpoints that read caught errors, clear them and manage the logging settings.
Overview
These seven endpoints read the errors that were caught, clear them, and set which books are kept.
A row is not one event but one problem. The same error repeated a hundred times stays one row, with its counter raised and its last-seen time refreshed. That is why records are addressed by signature rather than by id.
The records are a history: a row does not say "this is broken", it says "this was broken then". Before setting out to fix one, it is worth measuring whether it still happens.
Reference
Listing the Error Records
Returns the errors that were caught. Each row is not one event but one problem.
system or database.error, warning or fatal.curl -G 'https://panel.example.com/api/v1/admin/tools/logs/errors' \
-H "Authorization: Bearer $API_KEY" \
-d min_occurrence=5 \
-d hide_noise=1const url = new URL('https://panel.example.com/api/v1/admin/tools/logs/errors');
url.searchParams.set('min_occurrence', '5');
url.searchParams.set('hide_noise', '1');
const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();$url = 'https://panel.example.com/api/v1/admin/tools/logs/errors?' . http_build_query([
'min_occurrence' => 5,
'hide_noise' => 1,
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The list does not carry the BODY; read one record's detail to see the stack.
$errors = Api::Tools()->GetErrorLogs([], [
'min_occurrence' => 5,
'hide_noise' => 1,
])['data'];Error Record Detail
Returns one error together with its stack trace and the context of the request.
system or database. It defaults to system, so a database error has to be asked for.curl 'https://panel.example.com/api/v1/admin/tools/logs/errors/a1b2c3d4e5f600112233445566778899' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/tools/logs/errors/a1b2c3d4e5f600112233445566778899', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/tools/logs/errors/a1b2c3d4e5f600112233445566778899');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The context belongs to the FIRST sighting: later repeats raise the counter but leave the body.
$log = Api::Tools()->GetErrorLog(['signature' => 'a1b2c3d4e5f600112233445566778899'])['data'];Deleting Error Records
Deletes the errors you name, by their signatures.
curl -X DELETE 'https://panel.example.com/api/v1/admin/tools/logs/errors' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"signatures":["a1b2c3d4e5f600112233445566778899"]}'const res = await fetch('https://panel.example.com/api/v1/admin/tools/logs/errors', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ signatures: ['a1b2c3d4e5f600112233445566778899'] }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/tools/logs/errors');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['signatures' => ['a1b2c3d4e5f600112233445566778899']]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Compare the deleted count with what you sent: signatures matching nothing are skipped quietly.
$sent = array_column($errors, 'signature');
$response = Api::Tools()->DeleteErrorLogs(['signatures' => $sent]);
$missed = count($sent) - $response['data']['count'];Clearing by Date
Deletes error records older than the date you give, sweeping both books at once.
curl -X POST 'https://panel.example.com/api/v1/admin/tools/logs/errors/cleanup' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"before":"2026-01-01"}'const res = await fetch('https://panel.example.com/api/v1/admin/tools/logs/errors/cleanup', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ before: '2026-01-01' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/tools/logs/errors/cleanup');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['before' => '2026-01-01']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Clearing settled history makes every record after it a REAL signal.
$response = Api::Tools()->CleanupErrorLogs(['before' => '2026-01-01']);
$system = $response['data']['deleted']['system'];Importing the Leftover Files
Reads the old error files left on disk and moves them into the records.
curl -X POST 'https://panel.example.com/api/v1/admin/tools/logs/errors/rebuild' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/tools/logs/errors/rebuild', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/tools/logs/errors/rebuild');
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);// This is not an index rebuild; it imports from the old file store.
// What comes in passes the same scrubbing as a fresh capture.
$response = Api::Tools()->RebuildErrorLogs();Reading the Logging Settings
Returns which books are being kept and whether debugging is on.
curl 'https://panel.example.com/api/v1/admin/tools/logs/settings' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/tools/logs/settings', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/tools/logs/settings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Tools()->GetLogSettings();Writing the Logging Settings
Writes which books to keep and whether debugging is on.
curl -X PUT 'https://panel.example.com/api/v1/admin/tools/logs/settings' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"error_log":1,"module_log":0}'const res = await fetch('https://panel.example.com/api/v1/admin/tools/logs/settings', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ error_log: 1, module_log: 0 }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/tools/logs/settings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'error_log' => 1,
'module_log' => 0,
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Turn query logging on for your own address only: left open it fills the disk fast.
Api::Tools()->UpdateLogSettings([
'query_logging' => 1,
'query_logging_ips' => ['203.0.113.5'],
]);Pitfalls
A long error list does not mean that many open problems: most rows may already be fixed. To tell whether one still happens, read its counter, send a few requests to the surface in question, and read the counter again. If it has not moved there is nothing to fix.
The stack trace, the address and the user on the detail come from the first time the error was caught; later repeats only update the counter and the last-seen time. So you may be reading today's repeat through a context from months ago.
System errors and database errors are kept apart. The detail endpoint looks in the system book by default, so giving a database error's signature without naming the type finds nothing. The date-based clear, on the other hand, sweeps both.
Database query logging writes every request and grows fast when left on. Pair it with the address limit: give only your own address and it stays confined to your requests. In the same way, leaving error detail on in production shows internals to visitors.
Despite the name this endpoint rebuilds no index: it imports from the old file store left on disk. With nothing left to take it returns zero, which is not an error. The old bodies it takes in pass the same scrubbing as a fresh capture.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.