The Address Book
The six endpoints managing the account's billing profiles.
Overview
The address book holds the account's billing profiles. Each record carries a contact, an address and the tax rate that address falls under, and the invoices are raised from it.
One profile is always the default and the account never sits without one: the first added becomes it, and removing the default promotes the next in line.
The location fields are filled from the reference chain: a country code, a state number and a city number. Where the platform holds no data the same fields take free text.
Reference
Listing the Addresses
Returns the account's billing profiles.
curl 'https://panel.example.com/api/v1/client/addresses' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/addresses', {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
const billing = data.find((a) => a.is_default);$ch = curl_init('https://panel.example.com/api/v1/client/addresses');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The default contact ALWAYS comes first; take the first row rather than searching for it.
$rows = Kernel::internal('client:Addresses/GetAddresses', ['owner_id' => $uid])['data'];
$default = $rows[0] ?? null;Reading One Address
Returns one of the account's addresses.
curl 'https://panel.example.com/api/v1/client/addresses/12' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/addresses/${id}`, {
headers: { Authorization: `Bearer ${clientKey}` },
});
if (res.status === 404) return notYours();
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/addresses/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Someone else's address also answers NOT FOUND: this endpoint never says whether it exists.
$a = Kernel::internal('client:Addresses/GetAddress', ['owner_id' => $uid, 'id' => $id]);Adding an Address
Adds a new billing profile to the account.
curl -X POST 'https://panel.example.com/api/v1/client/addresses' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Jane","surname":"Cooper","email":"[email protected]","country":"TR","state":"34","city":"1441","address":"Sample St 42","zipcode":"34710"}'const res = await fetch('https://panel.example.com/api/v1/client/addresses', {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Jane', surname: 'Cooper', email: '[email protected]',
country: 'TR', state: String(stateId), city: String(cityId),
address: 'Sample St 42', zipcode: '34710',
}),
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/addresses');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($contact),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The FIRST address becomes the DEFAULT by itself; the account never sits without a profile.
$a = Kernel::internal('client:Addresses/CreateAddress', ['owner_id' => $uid] + $contact)['data'];
$isFirst = $a['is_default'];Updating an Address
Rewrites the address in full.
curl -X PUT 'https://panel.example.com/api/v1/client/addresses/12' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Jane","surname":"Cooper","email":"[email protected]","country":"TR","state":"34","city":"1441","address":"New St 7","zipcode":"34710"}'const cur = await fetch(`https://panel.example.com/api/v1/client/addresses/${id}`, {
headers: { Authorization: `Bearer ${clientKey}` },
}).then((r) => r.json());
const body = {
...cur.data,
state: String(cur.data.state.id || cur.data.state.name),
city: String(cur.data.city.id || cur.data.city.name),
address: 'New St 7',
};
await fetch(`https://panel.example.com/api/v1/client/addresses/${id}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});$ch = curl_init('https://panel.example.com/api/v1/client/addresses/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($contact),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The state and city are read as OBJECTS and sent as PLAIN VALUES: pull the number out before writing back.
$a = Kernel::internal('client:Addresses/GetAddress', ['owner_id' => $uid, 'id' => $id])['data'];
$a['state'] = (string) ($a['state']['id'] ?: $a['state']['name']);
$a['city'] = (string) ($a['city']['id'] ?: $a['city']['name']);
Kernel::internal('client:Addresses/UpdateAddress', ['owner_id' => $uid, 'id' => $id] + $a);Moving the Default
Makes this address the default billing profile.
curl -X POST 'https://panel.example.com/api/v1/client/addresses/12/default' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/addresses/${id}/default`, {
method: 'POST',
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/addresses/' . $id . '/default');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// There is no way to UNSET a default: making another address the default is the only road.
Kernel::internal('client:Addresses/SetDefaultAddress', ['owner_id' => $uid, 'id' => $other]);Removing an Address
Removes an address and hands the default on where needed.
curl -X DELETE 'https://panel.example.com/api/v1/client/addresses/12' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/addresses/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${clientKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/addresses/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Removing the default is NOT refused: the next address is promoted to default by itself.
Kernel::internal('client:Addresses/DeleteAddress', ['owner_id' => $uid, 'id' => $id]);
$now = Kernel::internal('client:Addresses/GetAddresses', ['owner_id' => $uid])['data'][0] ?? null;Pitfalls
The read endpoints return the state and city as an object carrying a number and a name, while the write endpoints expect a plain value. Sending a record straight back fails validation on those two fields. Pull the number out before writing, and send the name where the number is zero.
The update body is the whole address: the fields required on create are required here as well. Send the full record even to change the postcode alone. The one ease is the notification matrix, which keeps its saved value when left out.
Making an address the default pulls the account's country to that address's country. The country decides the tax rate and what some products show, so a simple-looking change can move prices. Think it through before defaulting a profile in another country.
While adding an address you can ask for the open invoices to move to it. Without that the invoices raised on the old address stay as they are and the customer sees two different addresses. Consider it on a change such as a new tax number.
Asking for another customer's address id answers 404 rather than a permission error. That is deliberate: the answer never reveals whether that id exists. Do not read a not-found as "it was removed".
The notification choices are kept at the account level and per contact: invoices can reach one person and support another. With the matrix left out a create opens every channel while an update keeps what was saved.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.