Module Settings and Calls

7 views Markdown

The six endpoints covering one module's settings, test, methods and records.

Overview

This article gathers the endpoints reaching inside a single module: reading and writing its settings, testing its connection, calling its own methods and seeing the records it keeps.

There are two ways to write settings and the difference matters. The settings endpoint follows the road the panel saves by, so the module's filters, validation and hooks all run. The configuration endpoint writes straight to the file.

Secrets never leave the server. Every password-like value comes back as a mask, and sending that same mask back means "leave this field alone".

Reference

Reading the Raw Configuration

get/api/v1/admin/modules/{group}/{module}/config
Modules/GetModuleConfig admin

Returns a module's settings on disk as they stand.

Response fields data — 4
groupstringThe group key.
keystringThe module key.
statusboolWhether the module is on.
settingsobjectThe raw settings. A value under a secret-looking key comes back masked.
Errors 3
unknown_group404No such module group.
module_not_found404No such module.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/modules/mail/Mailjet/config' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/modules/${group}/${key}/config`, {
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data } = await res.json();
const masked = Object.entries(data.settings).filter(([, v]) => v === '**********');
$ch = curl_init('https://panel.example.com/api/v1/admin/modules/' . $group . '/' . $key . '/config');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Secrets come back MASKED: this output is NOT a backup, and the keys do not travel to another installation.
$cfg = Api::Modules()->GetModuleConfig(['group' => $group, 'module' => $key])['data'];

Writing the Raw Configuration

put/api/v1/admin/modules/{group}/{module}/config
Modules/SaveModuleConfig admin no validation

Merges the keys you send into the module's settings file.

Body 2
settingsobjectThe keys to merge in. Sending the mask back keeps the secret.
statusintThe module state. It is written on the product and fraud groups.
Response fields data — 4
dataobjectThe configuration read afresh. Same shape as the read endpoint.
Errors 4
unknown_group404No such module group.
module_not_found404No such module.
settings_required422Neither settings nor a state was sent.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/modules/mail/Mailjet/config' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"from_name":"Support"}}'
const res = await fetch(`https://panel.example.com/api/v1/admin/modules/${group}/${key}/config`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ settings: { from_name: 'Support' } }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/modules/' . $group . '/' . $key . '/config');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['settings' => ['from_name' => 'Support']]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// This endpoint writes STRAIGHT to the file: the module's own validation and hooks DO NOT run.
// Pick the settings endpoint for daily work; this one is for recovery and migration.
Api::Modules()->SaveModuleConfig([
    'group' => $group, 'module' => $key, 'settings' => ['from_name' => 'Support'],
]);

Saving the Settings

put/api/v1/admin/modules/{group}/{module}/settings
Modules/UpdateModuleSettings admin the module validates

Saves the settings through the module's own validation.

Body 7
settingsobjectThe module's configuration fields. Sending the mask back keeps the secret.
statusintThe module state. It applies on the payment and fraud groups.
commission_ratestringThe rate added for paying this way. Payment alone.
force_convert_tointThe currency number amounts turn into. Payment alone.
accepted_countriesarrayOpen to these countries alone. Payment alone.
unaccepted_countriesarrayClosed to these countries. Payment alone.
change_subscription_feeintWhether changing a subscription carries a fee. Payment alone.
Response fields data — 11
dataobjectThe module read afresh. Same shape as the read endpoint in the catalogue article.
Errors 4
unknown_group404No such module group.
module_not_found404No such module.
module_error422The module turned the settings down. The message comes from the module.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/modules/payment/Stripe/settings' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"api_key":"sk_live_123"},"commission_rate":"2.5"}'
const res = await fetch(`https://panel.example.com/api/v1/admin/modules/${group}/${key}/settings`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    settings: { api_key: 'sk_live_123' },
    commission_rate: '2.5',
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/modules/' . $group . '/' . $key . '/settings');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['settings' => ['api_key' => $liveKey]]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The same road the panel saves by: the module's filters, validation and hooks DO run.
Api::Modules()->UpdateModuleSettings([
    'group' => 'payment', 'module' => 'Stripe',
    'settings' => ['api_key' => $liveKey], 'commission_rate' => '2.5',
]);

Testing the Connection

post/api/v1/admin/modules/{group}/{module}/test-connection
Modules/TestModuleConnection admin

Tries whether the module reaches the service behind it.

Body 1
settingsobjectThe settings to test with. A field you leave out comes from the saved configuration.
Response fields data — 3
groupstringThe group key.
keystringThe module key.
connectedboolWhether the connection was made.
Errors 4
unknown_group404No such module group.
module_not_found404No such module.
module_error422The test failed or the module offers none.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/modules/payment/Stripe/test-connection' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"api_key":"sk_test_123"}}'
const res = await fetch(`https://panel.example.com/api/v1/admin/modules/${group}/${key}/test-connection`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ settings: { api_key: candidate } }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/modules/' . $group . '/' . $key . '/test-connection');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['settings' => ['api_key' => $candidate]]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The test SAVES NOTHING: try a new key here first and write it with the settings endpoint when it passes.
$out = Api::Modules()->TestModuleConnection([
    'group' => $group, 'module' => $key, 'settings' => ['api_key' => $candidate],
])['data'];

if ($out['connected']) Api::Modules()->UpdateModuleSettings([
    'group' => $group, 'module' => $key, 'settings' => ['api_key' => $candidate],
]);

Running a Module Method

post/api/v1/admin/modules/{group}/{module}/methods/{method}
Modules/RunModuleMethod admin module specific

Calls a module's own panel method and returns what it gives.

Body
*objectA free body belonging to the module. It is handed to the method as it stands.
Response fields data — 4
groupstringThe group key.
keystringThe module key.
methodstringThe method that ran.
resultstringWhat the method gave. Text or an object, and the markup the panel shows can come as well.
Errors 5
unknown_group404No such module group.
module_not_found404No such module.
method_required422The method name is empty.
method_error422The method was not found or returned an error.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/modules/product/SampleProduct/methods/crud-list' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"page":1}'
const res = await fetch(`https://panel.example.com/api/v1/admin/modules/${group}/${key}/methods/${method}`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ page: 1 }),
});

const { data } = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/modules/' . $group . '/' . $key . '/methods/' . $method);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['page' => 1]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The result can be the PANEL's own output; its shape is the module's choice and not an API contract.
$out = Api::Modules()->RunModuleMethod([
    'group' => $group, 'module' => $key, 'method' => $method, 'page' => 1,
])['data'];

$isMarkup = is_string($out['result']);

Reading the Fraud Records

get/api/v1/admin/modules/{group}/{module}/records
Modules/GetFraudRecords admin fraud alone

Returns what a fraud module flagged.

Query 3
pageintWhich page.
limitintRecords per page. 100 at the most.
searchstringSearches the records.
Response fields data[] — 7 + meta — 4
idintThe record id.
user_idintThe id of the client flagged.
user_full_namestringThe client's name.
user_company_namestringThe client's company.
messagestringThe reason the module wrote.
ipstringThe address the action came from.
created_atstringWhen the record was written.
countintHow many came back. It comes back under meta.
totalintHow many there are.
pageintThe page you are on.
limitintThe page size.
Errors 4
unknown_group404No such module group.
module_not_found404No such module.
not_supported422A group other than fraud, or a module keeping no records.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/modules/fraud/MaxMind/records?limit=50' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/modules/fraud/${key}/records`, {
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data, meta } = await res.json();
const flagged = new Set(data.map((r) => r.user_id));
$ch = curl_init('https://panel.example.com/api/v1/admin/modules/fraud/' . $key . '/records');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Keeping records is the module's choice: calling without seeing it in the capabilities gives 422.
$m = Api::Modules()->GetModule(['group' => 'fraud', 'module' => $key])['data'];
if ($m['capabilities']['has_records'])
    $rows = Api::Modules()->GetFraudRecords(['group' => 'fraud', 'module' => $key])['data'];

Pitfalls

The configuration endpoint skips the module's validation

The configuration endpoint writes settings straight to the file: the module's field filters, checks and save hooks never run. A value that is not valid goes in without an error and breaks on first real use. Pick the settings endpoint for daily work; the configuration one is for recovery and migration.

A masked secret is not a backup

The read endpoints return secret-looking values as a mask and never give the real one. Exporting a module's configuration and moving it to another installation carries no keys; the fields fill with masks there and the module fails. Move the secrets separately.

The test saves nothing

The connection test uses the settings you send for that call alone and writes nothing even when it passes. Testing a new key and forgetting to save means the configuration you saw working never existed. Write it with the settings endpoint afterwards.

The method result has no contract

The method endpoint calls the module's panel method and hands the result over as it stands. That result can be an object or the markup the panel would show. The module decides the shape and a version can change it, so do not build an integration that parses it.

Records exist on some fraud modules alone

The records endpoint answers not_supported outside the fraud group, and not every module in that group keeps records either. Look at the capabilities in the module detail before calling: when it is absent there the endpoint gives an error rather than an empty list.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.