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.
1. Get a key
- Sign in at
/admin/loginas 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. - 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.
- 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
| Rule | What it means |
|---|---|
| Header only, HTTPS only | Authorization: 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. |
| Scopes | Each 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-Key | Every 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: true | On 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. |
| Limits | Per 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. |
| Paging | Lists 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. |
| Audit | Every 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:
| Call | Scope | What it does |
|---|---|---|
GET /me | none | The 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:read | Resellers with counts; one reseller with its connector state. |
POST /resellers, PUT /resellers/{id}/cad-systems, PATCH /resellers/{id} | resellers:write | Create 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 / write | A 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:read | Users with their companies and licences. Filters: resellerId, orgId, q. |
POST /users | users:write | Invite 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}/disable | users:write | Disable 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:read | Customer companies, their licences, members and seats. |
POST /orgs | orgs:write | Create 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}/members | users:write | Invite a person into a company (member or org_admin). |
GET /licences | licences:read | One 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 / unassign | Give 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 / unassign | The desktop-agent-only licence and the Tenant Admin licence. |
PUT/PATCH/DELETE /orgs/{id}/members/{userId}/seats/{cad} | licences:assign / unassign | Assign, 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-enable | licences:extend | Extend 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 .../test | connector:read / write | A 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}/reject | orders:approve | The 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).