Client API First Calls
The five endpoints called while connecting to the client surface.
Overview
The client API is for a customer managing their own account from an integration. It is a surface apart from the admin API: a different address and a different kind of key, with every call bounded to one customer.
This article answers the first three questions: is the surface up, what can my key do, and which values fill the address fields.
The address chain runs one way: the country code first, then the state number, then the city number. Each step wants the one before it, and any of them can come back empty.
Reference
The Health Check
Returns that the client surface is up, along with the server time.
curl 'https://panel.example.com/api/v1/client/ping'const res = await fetch('https://panel.example.com/api/v1/client/ping');
const { data } = await res.json();
if (! data.pong) reportOutage();$ch = curl_init('https://panel.example.com/api/v1/client/ping');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// This is SEPARATE from the admin surface's endpoint of the same name; one can be up while the other is not.
$up = Kernel::internal('client:System/Ping')['data']['pong'] ?? false;What the Key Is
Returns the key's identity, its permissions and the client it belongs to.
curl 'https://panel.example.com/api/v1/client/whoami' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/whoami', {
headers: { Authorization: `Bearer ${clientKey}` },
});
if (res.status === 403) return showWrongSurface();
const { data } = await res.json();
console.log(data.owner_id, data.permissions);$ch = curl_init('https://panel.example.com/api/v1/client/whoami');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// owner_id IS NOT PASSED on calls: the client comes from the key and writing it in the body changes nothing.
$me = Kernel::internal('client:System/Whoami', ['owner_id' => $ownerId])['data'];The Countries
Returns the country codes the profile and address endpoints take.
curl 'https://panel.example.com/api/v1/client/reference/countries' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/reference/countries', {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
renderCountryPicker(data);$ch = curl_init('https://panel.example.com/api/v1/client/reference/countries');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The client surface speaks the COUNTRY CODE; the country NUMBER from the admin surface is not taken here.
$rows = Kernel::internal('client:Reference/GetCountries', ['owner_id' => $uid])['data'];
$codes = array_column($rows, 'code');A Country's States
Returns a country's states with the numbers the address fields use.
curl 'https://panel.example.com/api/v1/client/reference/countries/TR/states' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/reference/countries/${code}/states`, {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
if (! data.length) useFreeTextState();$ch = curl_init('https://panel.example.com/api/v1/client/reference/countries/' . $code . '/states');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// AN EMPTY list is normal: with no states on that country the address field takes free text.
$states = Kernel::internal('client:Reference/GetStates', ['owner_id' => $uid, 'code' => $code])['data'];
$free = ! $states;A State's Cities
Returns a state's cities with the numbers the address fields use.
curl 'https://panel.example.com/api/v1/client/reference/states/34/cities' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/reference/states/${stateId}/cities`, {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/reference/states/' . $stateId . '/cities');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// A CITY IS REQUIRED on an address even when the list is empty: write free text rather than leaving it out.
$cities = Kernel::internal('client:Reference/GetCities', ['owner_id' => $uid, 'id' => $stateId])['data'];Pitfalls
The client surface takes a client key alone, and calling with an admin key gives audience_mismatch. That code means the wrong surface rather than a missing permission, so fixing the address instead of the key wastes time.
Every client endpoint is bounded by the key's owner. Writing a customer number into the body changes nothing, and asking for someone else's record answers not found. Use the admin surface when you need to reach more than one customer.
On the client surface a country is given as a two-letter code and the country number from the admin surface is not taken. The state and city want numbers. Filling all three the same way leads to a quiet validation error.
The state or city list can come back empty when the platform holds no data for that country or state. The address field then takes free text. A city stays required even with an empty list, and an address does not save without one.
The health endpoint wants no credentials, so a successful answer says nothing about your key working. Call both while wiring an integration up: the health endpoint proves the server and the key endpoint proves the credentials.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.