Orders
The four endpoints that show past orders, quote a price and place the order.
Overview
A customer can order on their own behalf, and that order completes without a browser. Payment is kept narrow for that reason: the account balance or a card the account has saved.
The flow runs in three steps: read the schema from the product detail, see the price with the preview, then place the order. The preview runs the same chain as the order endpoint and writes nothing.
Taking a domain does not belong here. A new domain is taken from the domain endpoints first and then named in the order as one you already own.
Reference
Listing the Orders
Returns the account's orders, newest first.
curl 'https://panel.example.com/api/v1/client/orders?status=active' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/orders?status=active', {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data, meta } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/orders?status=active');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The listing carries NO LINES: the items and the services born come on the detail endpoint.
$rows = Kernel::internal('client:Orders/GetOrders', ['owner_id' => $uid])['data'];Reading an Order in Full
Returns an order's lines, the services born and its invoice.
curl 'https://panel.example.com/api/v1/client/orders/918' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/orders/${id}`, {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
const ids = data.services.map((s) => s.id);$ch = curl_init('https://panel.example.com/api/v1/client/orders/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The services can already exist while the order still reads WAITING; follow the service state.
$o = Kernel::internal('client:Orders/GetOrder', ['owner_id' => $uid, 'id' => $id])['data'];
$live = array_filter($o['services'], fn ($s) => $s['status'] === 'active');Pricing an Order
Prices an order body without writing a single row.
curl -X POST 'https://panel.example.com/api/v1/client/orders/preview' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"items":[{"product_id":55,"cycle":"monthly","quantity":1}],"coupons":["WELCOME10"]}'const res = await fetch('https://panel.example.com/api/v1/client/orders/preview', {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ items, coupons }),
});
const { data } = await res.json();
showSummary(data.subtotal, data.discount, data.tax, data.total);$ch = curl_init('https://panel.example.com/api/v1/client/orders/preview');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['items' => $items]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The preview DOES NOT run the ordering gates: the terms, verification and balance go unchecked here.
$q = Kernel::internal('client:Orders/PreviewOrder', ['owner_id' => $uid, 'items' => $items])['data'];
$due = $q['total']['amount'];Placing the Order
Places the order and collects payment in the same call.
curl -X POST 'https://panel.example.com/api/v1/client/orders' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"terms":true,"items":[{"product_id":55,"cycle":"monthly"}],"payment":{"method":"balance"}}'const res = await fetch('https://panel.example.com/api/v1/client/orders', {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
terms: true,
items,
payment: { method: 'balance' },
}),
});
const { data } = await res.json();
if (data.payment.status === 'failed') showPayLater(data.invoice);$ch = curl_init('https://panel.example.com/api/v1/client/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'terms' => true, 'items' => $items,
'payment' => ['method' => 'balance'],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// A 201 DOES NOT MEAN PAID: a declined card still leaves the order and its unpaid invoice, so read the payment state.
$o = Kernel::internal('client:Orders/CreateOrder',
['owner_id' => $uid, 'terms' => true, 'items' => $items,
'payment' => ['method' => 'card']])['data'];
if ($o['payment']['status'] !== 'paid') $notify($o['invoice']['invoice_id']);Pitfalls
The order endpoint answers with success even on a failed collection, because the order was built: a declined card leaves the order and its unpaid invoice standing. Read whether it was paid from the payment state in the answer. Only the provider's plain approval counts as paid, and an answer wanting a further step fails here.
The preview runs the whole pricing chain and skips the gates that belong to the moment of ordering: accepting the terms, account verification, the policy hook and whether the balance covers it. A body passing the preview cleanly can still be refused by the order endpoint.
A line's domain axis takes a domain you already own or a free subdomain alone. Asking to register or transfer is refused with a plain error. The right order is to take it from the domain endpoints first and then name it in the order as owned.
An order field wanting a file wants encoded content, and giving an address is refused. That is a deliberate security decision: the server never downloads an address a caller hands it. Send the file with the request.
An order reads waiting right after collection and stays there until provisioning moves it on. The services can already exist by then. Follow the progress from the service states in the detail rather than from the order's own.
The product prices, the preview total and the order total all come in one currency: the account's wallet currency. No conversion is needed to compare them with the balance. The display choice on the profile does not move these figures.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.