Recurring Expenses
The six endpoints that define the fixed outgoings repeating each month.
Overview
Recurring expenses define the payments that come round every month: server rent, a licence fee, a subscription. What lives here is not an expense but a rule that brings expenses into being.
Each rule carries a day and a time. When the scheduled job reaches that moment the rule runs and writes that month's expense into the cash book. So the number of records here is not the number in the book.
The summary looks forward: it says how much fixed outgoing an installation has each month. To see what was actually paid, look at the cash book.
Reference
Listing the Expenses
Returns the expenses that repeat every month, filtered.
curl 'https://panel.example.com/api/v1/admin/invoices/periodic' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/invoices/periodic', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/invoices/periodic');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// These are RULES rather than records: each one brings a cash expense into being monthly.
$rules = Api::Invoices()->GetPeriodicExpenses()['data'];Adding an Expense
Defines an expense to be charged every month by itself.
curl -X POST 'https://panel.example.com/api/v1/admin/invoices/periodic' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"currency_id":840,"amount":49.9,"day":1,"time":"09:00","description":"Aylik sunucu gideri"}'const res = await fetch('https://panel.example.com/api/v1/admin/invoices/periodic', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
currency_id: 840,
amount: 49.9,
day: 1,
time: '09:00',
description: 'Monthly server cost',
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/invoices/periodic');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'currency_id' => 840,
'amount' => 49.9,
'day' => 1,
'time' => '09:00',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Picking the thirty-first can leave the record unprocessed in the SHORT months.
Api::Invoices()->CreatePeriodicExpense([
'currency_id' => 840,
'amount' => 49.9,
'day' => 1,
'time' => '09:00',
]);Reading the Summary
Returns the total of the fixed monthly outgoings.
curl 'https://panel.example.com/api/v1/admin/invoices/periodic/summary' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/invoices/periodic/summary', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/invoices/periodic/summary');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// This total looks FORWARD: it says what will be paid each month, not what was.
$plan = Api::Invoices()->GetPeriodicSummary()['data'];Reading One Expense
Returns a single recurring expense.
curl 'https://panel.example.com/api/v1/admin/invoices/periodic/8' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/invoices/periodic/${periodicId}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/invoices/periodic/' . $periodicId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The record does NOT carry when it last ran; read that from the cash book instead.
$rule = Api::Invoices()->GetPeriodicExpense(['periodic_id' => $id])['data'];Updating an Expense
Changes a recurring expense's amount or its timing.
curl -X PATCH 'https://panel.example.com/api/v1/admin/invoices/periodic/8' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"amount":59.9,"day":5,"time":"10:30"}'const res = await fetch(`https://panel.example.com/api/v1/admin/invoices/periodic/${periodicId}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount: 59.9, day: 5, time: '10:30' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/invoices/periodic/' . $periodicId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['amount' => 59.9, 'day' => 5]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The change does NOT reach back: what already ran this month keeps the old amount.
Api::Invoices()->UpdatePeriodicExpense(['periodic_id' => $id, 'amount' => 59.9]);Removing an Expense
Removes a recurring expense.
curl -X DELETE 'https://panel.example.com/api/v1/admin/invoices/periodic/8' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/invoices/periodic/${periodicId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/invoices/periodic/' . $periodicId);
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);// A delete leaves PAST cash records alone; it only stops the rule running again.
Api::Invoices()->DeletePeriodicExpense(['periodic_id' => $id]);Pitfalls
The thirty-first is not in every month, and neither are the thirtieth and the twenty-ninth in some. A rule set to such a day can go unprocessed in those months and the expense never reaches the book. For an outgoing that has to run monthly, pick an early day.
Changing the amount or the day touches only what comes after. An expense already processed this month stays in the book at the old amount, and the new one shows next month. To correct the past, update the record in the cash book as a separate step.
Deleting a rule only stops it running again; the expense records it made up to that day stay in the book. That is the right behaviour, because those payments really happened. Clearing the past means removing those cash records as a separate step.
A recurring expense record does not say when it last ran. Telling whether a rule ran this month means looking in the cash book for a record matching its description. Weigh that when writing a check: the list here gives no run history on its own.
The summary here says what will be paid each month, while the cash summary says what was paid this month. The two do not match and are not meant to: a rule running on the twentieth is not in the book on the tenth. Keep them on separate lines in a report.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.