Currencies and Rates
The eight endpoints for the currencies, their rates and the rate provider.
Overview
An installation has one base currency, and the others carry their rate against it. Prices, invoices and reports all convert through that point.
Rates are either written by hand or come from a provider module. You pick the provider and set how often it runs, and a scheduled task refreshes the rates. An endpoint for pulling them by hand sits alongside.
A currency can be tied to countries: a visitor's country decides which one they see. A country belongs to one currency at a time.
Reference
Listing the Currencies
Returns every currency on the installation with its rate.
curl 'https://panel.example.com/api/v1/admin/financial/currencies' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/currencies', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();
const base = data.find((c) => c.local);$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currencies');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Rates run against the BASE currency; converting between two others passes through it.
$all = Api::Financial()->GetCurrencies()['data'];
$base = current(array_filter($all, fn ($c) => $c['local']));Reading the Rate Settings
Returns which provider supplies the rates and how often they refresh.
curl 'https://panel.example.com/api/v1/admin/financial/currency-settings' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/currency-settings', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currency-settings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// An OLD last-run time means stale rates; the refresh may have quietly stopped.
$s = Api::Financial()->GetCurrencySettings()['data'];Writing the Rate Settings
Changes the rate provider and how the refresh runs.
curl -X PUT 'https://panel.example.com/api/v1/admin/financial/currency-settings' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"module":"WAtlas","auto_rate_enabled":true,"update_period":"day"}'const res = await fetch('https://panel.example.com/api/v1/admin/financial/currency-settings', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
module: 'WAtlas',
auto_rate_enabled: true,
update_period: 'day',
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currency-settings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'module' => 'WAtlas',
'auto_rate_enabled' => true,
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Put a provider through the test endpoint BEFORE switching; a bad key gives quiet staleness.
Api::Financial()->TestCurrencyModule(['module' => 'WAtlas']);
Api::Financial()->UpdateCurrencySettings(['module' => 'WAtlas']);Testing the Provider
Tests a rate provider by pulling a sample rate from it.
curl -X POST 'https://panel.example.com/api/v1/admin/financial/currency-modules/test' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"module":"WAtlas"}'const res = await fetch('https://panel.example.com/api/v1/admin/financial/currency-modules/test', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ module: 'WAtlas' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currency-modules/test');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['module' => 'WAtlas']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The test connects LIVE and saves nothing; a provider not yet chosen can be tried too.
$r = Api::Financial()->TestCurrencyModule([
'module' => 'WAtlas',
'module_data' => ['WAtlas' => ['api_key' => $key]],
])['data'];Refreshing the Rates
Pulls the rates from the provider by hand.
curl -X POST 'https://panel.example.com/api/v1/admin/financial/currencies/sync' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/currencies/sync', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();
if (! data.rates_changed) console.warn(data.reason);$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currencies/sync');
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);// A successful call does not mean the rates MOVED; read the reason field.
$r = Api::Financial()->SyncCurrencyRates()['data'];
if (! $r['rates_changed']) error_log($r['reason'] ?? 'skipped');Reading One Currency
Returns a single currency.
curl 'https://panel.example.com/api/v1/admin/financial/currencies/4' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/financial/currencies/${id}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currencies/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The base currency's rate is ALWAYS one; trying to change it means nothing.
$c = Api::Financial()->GetCurrency(['id' => $id])['data'];Updating a Currency
Changes a currency's look, its rate and what it is tied to.
curl -X PATCH 'https://panel.example.com/api/v1/admin/financial/currencies/4' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"US Dollar","prefix":"$","hidden":false}'const res = await fetch(`https://panel.example.com/api/v1/admin/financial/currencies/${id}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'US Dollar', prefix: '$', hidden: false }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currencies/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['name' => 'US Dollar', 'prefix' => '$']),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Do not read the base field as ONE MORE switch: it moves the installation's money base
// and every rate is worked out again.
Api::Financial()->UpdateCurrency(['id' => $id, 'prefix' => '$']);Switching a Currency On and Off
Decides whether a currency can be used at all.
curl -X PUT 'https://panel.example.com/api/v1/admin/financial/currencies/4/status' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"status":true}'const res = await fetch(`https://panel.example.com/api/v1/admin/financial/currencies/${id}/status`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: true }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/currencies/' . $id . '/status');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['status' => true]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Switching off leaves PAST invoices in that currency alone; it stops new choices.
Api::Financial()->SetCurrencyStatus(['id' => $id, 'status' => false]);Pitfalls
The base-currency field in the update body is no ordinary option. It moves the installation's money base, writes to the settings file and has every rate worked out again. Prices keep the same numbers while now meaning a different unit. Sending this field by accident is among the costliest mistakes here.
With the refresh on, writing a rate by hand is a passing correction: the provider writes over it on the next run. For a rate that holds, switch the refresh off first. This is why a rate seems to "come back" after being set by hand.
The refresh endpoint counts as successful even when it returns without pulling anything, and names the reason in a separate field. The provider may not have answered, the interval may not have passed, or the setting may be off. Monitoring that reads only the response code will not notice rates gone stale for months.
Tying a country to a currency while it sits on another gets the call refused. The country list replaces what was there, so a country left out drops its tie. When changing them, read the current list and add to it.
The test endpoint opens a live connection and saves nothing. A provider not yet chosen can be tried with settings passed in. Switching provider without testing leaves the rates quietly unrefreshed. No error shows, and only the last-run time gives it away by standing still.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.