Create a managed client account under the calling partner.
const url = 'https://api.siteqwality.com/partner/accounts';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"display_name":"example","contact_email":"example","contact_name":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.siteqwality.com/partner/accounts \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "display_name": "example", "contact_email": "example", "contact_name": "example" }'The client is a perfectly ordinary account with a parent pointer: it has
its own monitors, its own status pages, its own quotas and its own
notification routing, and the partner works in it through the
X-SQ-Account header. Three things make it not-ordinary, all decided here
rather than inside the shared provisioning seam:
- No Stripe customer. The partner is billed on one subscription; a client is never billed directly, so it never gets a customer object to leak or reconcile.
- No welcome email. The contact never asked us for an account. A “welcome to SiteQwality” email out of nowhere is the partner’s relationship to manage, not ours.
- A Stytch organization with no member and no session. Nobody is
signing in yet. The organization exists up front so the partner’s later
invite has somewhere to land, and the local user row (which the account
needs, for
primary_user_id) carries a NULLstytch_member_iduntil then.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”Body for POST /partner/accounts.
object
The partner’s label for this client, e.g. “Acme Corp”. Not shown to the client and not client-editable: it is how the partner console lists them, and it names the client’s Stytch organization.
The client’s real contact address. It becomes the child account’s primary user and the address its default notification group alerts, so an outage on the client’s site reaches the client rather than the partner.
Must be distinct across the partner’s client accounts. See
Database::partner_client_account_id_by_contact_email for why that is
a pricing rule and not a data-hygiene preference.
The contact’s name, when the partner knows it. Absent is normal.
Example generated
{ "display_name": "example", "contact_email": "example", "contact_name": "example"}Responses
Section titled “ Responses ”Client account created
One managed client account, as the console lists it and as create answers.
object
The client’s account id. This is the value the partner sends as
X-SQ-Account to open the client in the ordinary dashboard.
Nullable because accounts.primary_user_id carries no foreign key; see
DbPartnerChildAccount::contact_email.
Convenience mirror of suspended_at.is_some(), so a client list can
render the badge without parsing a timestamp.
Monitor counts for one client, in the same state-aware sense
GET /monitors/totals uses: a paused monitor counts in total and
paused and in neither online nor down.
object
Example generated
{ "account_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "display_name": "example", "contact_email": "example", "created_at": "2026-04-15T12:00:00Z", "suspended": true, "suspended_at": "2026-04-15T12:00:00Z", "monitors": { "total": 1, "online": 1, "down": 1, "paused": 1 }}Invalid request body
Missing or invalid authentication
No client slot left on the partner plan
Caller is not an admin, or the account is not in the partner program
That contact address already belongs to one of your clients, or to another SiteQwality account
Internal server error