Management API

A website admin can let a script or a local AI agent do the routine provisioning work: add a reseller or a reseller's staff, invite a user, create a customer company, hand out or take back a CAD licence, extend a trial. It is a small HTTP API under /api/v1/manage, protected by an API key that only a website admin can create.

The key is a password. It is shown once, when it is created, and only a one-way hash of it is stored on our server. Keep it in Windows Credential Manager (below), never paste it into a chat, a ticket or a file. Anyone who holds it can do what its scopes allow until you revoke it.

1. Get a key

  1. Sign in at /admin/login as a website admin. Creating a key needs a sign-in from the last 10 minutes; the page links you to sign in again when yours is older.
  2. Open Management > API Keys. Give the key a name, tick only the scopes it needs, and optionally set an expiry, an allowed-IP list and a calls-per-minute limit. A key does not expire unless you set one.
  3. Press Create key. The key (shk_ followed by 40 characters) is shown once with a Copy button. Leaving the page removes it. If you lose it, revoke it and create another (or press Rotate, which creates a new key with the same settings and revokes the old one at once).

A key belongs to the admin who created it. If that admin is disabled or loses the admin grant, the key stops working on the next call. The page lists every key with its last use and address, and a usage list of its latest calls (route, ids, status, time - never a request body or the key).

2. Store it

On the PC that will call the API, once:

.\Invoke-SphereManage.ps1 -SaveKey

It asks for the key with hidden input (the key is never echoed, never a command-line argument) and writes the generic credential SphereHarness-ManageKey. Or add that credential by hand in Control Panel > Credential Manager > Windows Credentials > Add a generic credential (user name shk, password = the key). Do not use cmdkey /pass:: it leaves the key in the process list and the shell history.

Then call the API with the helper script tools\manage-api\Invoke-SphereManage.ps1. It reads the key from Credential Manager, sends it in the header and prints only the server's JSON, so the key never appears in a command line, a log or a conversation with an AI agent:

.\Invoke-SphereManage.ps1 GET /me
.\Invoke-SphereManage.ps1 GET "/licences?status=expired&limit=20"
.\Invoke-SphereManage.ps1 POST /licences/extend -Body '{"userIds":[42],"days":14,"reason":"Evaluation runs on"}' -DryRun

Raw HTTP, if you must (read the key into a variable from Credential Manager; never type it on the command line):

curl -s https://sphereharness.io/api/v1/manage/me -H "Authorization: Bearer $KEY"

The key goes in the Authorization header only. A key in the address (query string) is never read.

3. The rules every call follows

RuleWhat it means
Header only, HTTPS onlyAuthorization: Bearer shk_.... Anything else (a user token, a licence key, a cookie, a query string) is a 401. A key opens nothing outside /api/v1/manage, and no sign-in opens the manage API.
ScopesEach key holds only the scopes you ticked. A call outside them is 403 forbidden_scope. There is no scope for managing keys: a key cannot create, list or revoke keys.
Idempotency-KeyEvery call that creates something (and every extend) needs an Idempotency-Key header. Repeating the same key on the same call within 24 hours returns the first answer (Idempotency-Replayed: true) and changes nothing. Reusing it for a different call is 409 idempotency_mismatch.
X-Dry-Run: trueOn any change: the server validates and runs it in a transaction that is rolled back. Nothing is saved, nothing is e-mailed, and the answer carries "dryRun": true. Use it to rehearse.
LimitsPer key: the calls per minute you set (10 to 600, default 60), answered 429 with Retry-After. Per address: 120 calls a minute on the whole API, and 20 refused keys in 10 minutes block that address for a while. Bodies are limited to 64 KB.
PagingLists take ?limit=50 (at most 200) and &cursor= and answer { "items": [...], "nextCursor": "..." }.
Errors{ "error": "code", "message": "one sentence", "details": {...}, "traceId": "..." }. Codes: unauthorized 401, forbidden_scope 403, not_found 404, validation 400, conflict 409, edition_mismatch 409, idempotency_mismatch 409, payload_too_large 413, rate_limited 429.
AuditEvery call is logged (key, route, ids, status, time). Every change is also written to the audit trail with the admin who owns the key, the key's prefix and the ids it touched - never the key, a password or a request body.

Nothing is ever returned or accepted as a password. A user created through the API gets an e-mail with a link to choose their own.

4. What you can do

The full, machine-readable list (with the scope of each call) is the OpenAPI document at /api/v1/manage/openapi.json; an AI agent can read it directly. In short:

CallScopeWhat it does
GET /menoneThe key (no secret), its scopes and limits, and which edition of the site and which CAD systems this host offers.
GET /resellers, GET /resellers/{id}resellers:readResellers with counts; one reseller with its connector state.
POST /resellers, PUT /resellers/{id}/cad-systems, PATCH /resellers/{id}resellers:writeCreate a reseller (optionally with its primary admin invited by e-mail), set its CAD designation, name its primary admin.
GET/POST /resellers/{id}/members, PATCH/DELETE /resellers/{id}/members/{userId}reseller-users:read / writeA reseller's staff: add (an existing account, or invite a new one), change the role (admin or employee), remove the membership. The primary admin cannot be removed.
GET /users, GET /users/{id}users:readUsers with their companies and licences. Filters: resellerId, orgId, q.
POST /usersusers:writeInvite a person: through a reseller (resellerId: invited, joined, or a transfer request), into a company (orgId), or a new account by itself. Never a password.
POST /users/{id}/disableusers:writeDisable an account and end its sessions (a reason is required). A website admin account cannot be disabled this way.
GET /orgs, GET /orgs/{id}orgs:readCustomer companies, their licences, members and seats.
POST /orgsorgs:writeCreate a company for a reseller. Its trial waits for approval, like a company a reseller creates. Refused (409 edition_mismatch) on a host that offers none of the reseller's CAD systems.
POST /orgs/{id}/membersusers:writeInvite a person into a company (member or org_admin).
GET /licenceslicences:readOne item per subscription and CAD: seats, used, status (active, trial, expired, suspended, cancelled), days remaining, who holds the seats. Filters include status, cad, expiringBefore.
PUT/DELETE /users/{id}/cads/{cad}licences:assign / unassignGive or take back a CAD system for a direct user. (A member of a company is given a seat in the company instead.)
PUT/DELETE /users/{id}/products/{agent|tenantadmin}licences:assign / unassignThe desktop-agent-only licence and the Tenant Admin licence.
PUT/PATCH/DELETE /orgs/{id}/members/{userId}/seats/{cad}licences:assign / unassignAssign, set the number of, or release a company member's seat. Refused with the reason when all seats are taken.
POST /licences/extend, POST /licences/re-enablelicences:extendExtend trials by up to the server's per-call maximum, or restart expired ones. A reason (3 to 500 characters) is required and is written to the audit trail. Paid licences have no term to extend.
GET/PUT/DELETE /resellers/{id}/support-connector, POST .../testconnector:read / writeA reseller's own support endpoint. The API key and webhook secret are write-only: no call returns them.
GET /orders, POST /orders/{id}/approve, POST /orders/{id}/rejectorders:approveThe licence-order queue.

5. Two hosts

A key works on both sphereharness.io and the SphereWorks host (the same data). GET /me tells you which edition and which CAD systems the host you called offers. The CAD designation of a reseller applies on both.

6. Turn a key off

Press Revoke on the API Keys page: the key is refused from the next call. If a key may have leaked, revoke it first, then look at its usage list and the audit trail. The whole API can be switched off on the server (the address then answers 404).