Skip to content

Client accounts

A client account is one of your managed clients. You create it, you choose what it is entitled to, you build its monitors and its status page inside it, and you remove it when the engagement ends.

In the dashboard the whole surface is Clients in the sidebar. Everything there is also an API call, which is what the rest of this page documents.

Every endpoint below requires the Admin or Super Admin role and a session token. Machine credentials are refused; see Roles and access.

  1. Create the account.

    Terminal window
    curl -X POST https://api.siteqwality.com/partner/accounts \
    -H "Authorization: Bearer $SESSION_JWT" \
    -H "Content-Type: application/json" \
    -d '{
    "display_name": "Acme Corp",
    "contact_email": "ops@acme.example",
    "contact_name": "Ada Lovelace"
    }'
    FieldRequiredNotes
    display_nameyesYour label for the client, up to 128 characters. Not shown to the client and not client-editable.
    contact_emailyesThe client’s real contact address. Must be unused across all of SiteQwality, and distinct from your other clients’ contacts.
    contact_namenoFree text, up to 300 characters.

    The response is the client summary, and account_id is the value you will use everywhere else:

    {
    "data": {
    "account_id": "0f3c9a1e-...",
    "display_name": "Acme Corp",
    "contact_email": "ops@acme.example",
    "created_at": "2026-08-06T09:00:00Z",
    "suspended": false,
    "suspended_at": null,
    "monitors": { "total": 0, "online": 0, "down": 0, "paused": 0 }
    }
    }
  2. Give it a bundle, or it stays on the free tier. See Set a client’s bundle.

  3. Build its monitoring by working inside the account with X-SQ-Account.

What creation does, and deliberately does not do

Section titled “What creation does, and deliberately does not do”
DoesDoes not
Creates the account, its first user, a default notification group and an email channel pointed at contact_emailSend the contact a welcome email, or any email
Creates an identity-provider organization so a later invite has somewhere to landInvite anybody or create a session
Records you as the account’s partnerCreate a payment method or a billing customer

The contact never asked us for an account, so we never write to them out of nowhere. Whether and when your client hears from SiteQwality is your call: invite them from inside the account when you want them to have a login, and they get an ordinary invitation at that point.

Why every client needs its own contact address

Section titled “Why every client needs its own contact address”

A repeat contact address is refused with 409, whether it already belongs to one of your clients or to any other SiteQwality account. Addresses are compared case-insensitively, so Ada@Acme.example collides with ada@acme.example.

This is a pricing rule, not data hygiene. The partner rate is below what the same entitlements cost retail, so client accounts have to be actual distinct clients rather than five slots pointed at the same organization.

ResponseMeaning
400Missing or malformed field. The message names it.
402No client slot left on your plan. Remove a client or add slots.
403You are not an Admin, or your account is not in the partner program.
409That contact address is already in use, by one of your clients or by another SiteQwality account.
Terminal window
curl https://api.siteqwality.com/partner/accounts \
-H "Authorization: Bearer $SESSION_JWT"

Returns { "data": { "clients": [...] } }, newest first, unpaged. Each row carries the same fields as the create response, including live monitor counts in the same state-aware sense GET /monitors/totals uses: a paused monitor counts in total and paused, and in neither online nor down.

Removed clients are absent. So are their monitor counts, which means the list is a fair basis for reconciling your invoice.

Terminal window
curl -X PUT https://api.siteqwality.com/partner/accounts/$CLIENT_ID/plan \
-H "Authorization: Bearer $SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"bundle": "client_pro"}'

Two bundles exist. You pick one; you do not compose packs individually, because the wholesale rate is flat per client and each bundle is one of the two compositions that rate was priced against.

client_standardclient_pro
Check executions500K/month500K/month
Status page on the client’s own domainyesyes
White-labelled status pageyesyes
Synthetic browser checks1,000 minutes/month
RUM (Observe)25K sessions/month
RUM (Analyze)1K sessions/month
Session replay1K sessions/month

client_pro is everything in client_standard plus the rows below it. A client with no bundle set is on the free tier.

The response tells you what changed:

{
"data": {
"account_id": "0f3c9a1e-...",
"bundle": "client_pro",
"packs": [ { "pack_id": "...", "product": "uptime", "name": "500K Check Executions" } ],
"granted": 4,
"cancelled": 0
}
}

Two things this never touches. Anything the client bought retail on their own account stays, and so does anything our staff granted them. Only the bundle’s own entitlements are added and removed.

An unknown bundle name is a 422.

Terminal window
curl https://api.siteqwality.com/partner/accounts/$CLIENT_ID/usage \
-H "Authorization: Bearer $SESSION_JWT"

Per-product counters for that client, in the product’s customer-facing unit, matching exactly what the client would see on their own billing page.

{
"data": {
"account_id": "0f3c9a1e-...",
"products": [
{
"product": "uptime",
"current_usage": 128400,
"quota_limit": 500000,
"hard_stopped": false,
"unit": "executions",
"period": "2026-08"
}
]
}
}
Terminal window
curl -X DELETE https://api.siteqwality.com/partner/accounts/$CLIENT_ID \
-H "Authorization: Bearer $SESSION_JWT"

Immediately, and in this order of what you will notice:

  • the client’s slot is freed, so you can add a replacement in the same minute,
  • the client’s checks stop running,
  • the client’s users can no longer sign in,
  • the client can no longer be opened with X-SQ-Account, and
  • the client disappears from GET /partner/accounts.

Removing a client that is already removed, or that was never yours, is a 404.

Bulk provisioning is an ordinary loop over the create endpoint. There is no batch endpoint, and one is not planned: creation reaches an external identity provider per account, so a batch call would be the same work with a worse failure mode.

#!/usr/bin/env bash
# clients.csv: display_name,contact_email,contact_name
set -euo pipefail
while IFS=, read -r name email contact; do
response=$(curl -sS -w '\n%{http_code}' \
-X POST https://api.siteqwality.com/partner/accounts \
-H "Authorization: Bearer $SESSION_JWT" \
-H "Content-Type: application/json" \
-d "$(jq -nc --arg n "$name" --arg e "$email" --arg c "$contact" \
'{display_name:$n, contact_email:$e, contact_name:$c}')")
status=$(tail -n1 <<<"$response")
body=$(sed '$d' <<<"$response")
case "$status" in
200) echo "ok $email $(jq -r '.data.account_id' <<<"$body")" ;;
409) echo "skip $email already exists" ;;
402) echo "STOP out of client slots"; exit 1 ;;
429) echo "STOP rate limited, rerun later"; exit 1 ;;
*) echo "FAIL $email $status $body"; exit 1 ;;
esac
sleep 1
done < clients.csv
  1. One at a time, with a pause. Roughly one create per second is the right shape. Creation is not a cheap insert: it provisions an identity-provider organization and writes several rows in one transaction, so parallelising it buys little and makes partial failures harder to read.

  2. Treat 429 as a full stop, not a retry-in-a-tight-loop. API rate limits are shared across the whole API, so hammering through a limit degrades your own dashboard as well. Back off and rerun.

  3. Make the loop resumable. Treat 409 as “already done” and continue, as the script above does. Then a rerun after any failure is safe, and you do not need to track how far you got.

  4. Stop on 402 rather than continuing. Running out of slots mid-run is a commercial answer, not a transient error. Every subsequent create will fail the same way.

  5. Set bundles in a second pass, after every account exists. PUT .../plan is idempotent, so that pass is rerunnable on its own and a failure in it never leaves an account half-created.