Endpoints
The sandbox harness lives under /sandbox/*: mint tokens, flip kill switches, inspect what the internal-API mirror stored, and proxy read-only requests to the real API. This page is the reference for those endpoints, plus the sandbox's token decision logic.
The /internal/* endpoints — verify, validate, wipe results, assets, notes,
tests, chassis maps — are a faithful mirror of the real internal API, and
their request/response contracts are documented once, on the
Internal API page. This page documents only what is
sandbox-specific: the harness endpoints around the mirror and the decision
ladder they let you exercise.
All examples use https://api.averase.com. Every /sandbox/* endpoint accepts the optional Authorization: Bearer <ApiToken> header described in the overview; without it you're in anonymous mode and supply account fields yourself. Unhandled errors return 500 {"success": false, "errors": [{"code": 7000, "message": "Internal Server Error"}]}.
The token row
Sandbox tokens are plain rows — the jti is the credential, standing in for the signed device token a real ISO carries.
Properties
- Name
prefix_id- Type
- string
- Description
Public identifier,
ltok_….
- Name
jti- Type
- string
- Description
Unique UUID. This is what device calls present; it doubles as the row's key for
PATCH /sandbox/tokens/:jti.
- Name
account_id- Type
- string
- Description
Owning account,
acct_….
- Name
account_name- Type
- string
- Description
Display name of the account, echoed as
accountin verify responses.
- Name
account_active- Type
- boolean
- Description
Stand-in for the real system's subscription state. Set it to
falseto simulate a lapsed subscription.
- Name
user_email- Type
- string
- Description
Email of the user the token belongs to; the sandbox also uses it as the
operatoron wipe reports.
- Name
product- Type
- string
- Description
engineorhotswap.
- Name
status- Type
- string
- Description
active,revoked,suspended, ordisabled.
- Name
quota_limit- Type
- integer
- Description
Maximum metered check-ins;
nullmeans unlimited.
- Name
use_count- Type
- integer
- Description
Metered check-ins consumed so far. Incremented atomically by
POST /internal/tokens/validate.
- Name
last_seen_at- Type
- timestamp
- Description
Heartbeat, bumped by a successful verify.
- Name
revoked_at- Type
- timestamp
- Description
When the token was revoked, if ever.
- Name
created_at- Type
- timestamp
- Description
When the token was minted.
Mint a token
Creates a token row. Every body field is optional — with a valid API key
attached, account_id, account_name, and user_email default to your
real account.
Optional attributes
- Name
account_name- Type
- string
- Description
Account display name for the row.
- Name
account_id- Type
- string
- Description
Account identifier the token binds to.
- Name
user_email- Type
- string
- Description
Email stamped on the token (and used as
operatorin wipe reports).
- Name
product- Type
- string
- Description
engineorhotswap. Defaults toengine.
- Name
quota_limit- Type
- integer
- Description
Wipe quota. Defaults to
null(unlimited).
- Name
status- Type
- string
- Description
Initial status. Defaults to
active.
Request
curl https://api.averase.com/sandbox/tokens \
-H "Content-Type: application/json" \
-d '{
"account_name": "Test Co",
"account_id": "acct_demo",
"user_email": "ops@testco.example",
"product": "engine",
"quota_limit": 5
}'
Response (201)
{
"prefix_id": "ltok_01J4QY7R5N8T2VW9XA3BC4DEFG",
"jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
"account_id": "acct_demo",
"account_name": "Test Co",
"account_active": true,
"user_email": "ops@testco.example",
"product": "engine",
"status": "active",
"quota_limit": 5,
"use_count": 0,
"last_seen_at": null,
"created_at": "2026-08-12T16:58:44Z"
}
List tokens
Returns all token rows, newest first. Like every sandbox listing, it is not account-scoped — you see every row in the local database.
Request
curl https://api.averase.com/sandbox/tokens
Response (200)
[
{
"prefix_id": "ltok_01J4QY7R5N8T2VW9XA3BC4DEFG",
"jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
"status": "active",
"use_count": 1
// …
}
]
Update a token
The kill switches. Flip any combination of the four fields to steer the next verify or validate down a deny path — this is the sandbox equivalent of the ISO control column, the staff kill switch, a lapsed subscription, and quota exhaustion all in one.
Optional attributes
- Name
status- Type
- string
- Description
active,revoked,suspended, ordisabled.
- Name
quota_limit- Type
- integer
- Description
New quota;
nullfor unlimited.
- Name
use_count- Type
- integer
- Description
Set the consumed count directly — the quick way to reach
quota_exceededwithout burning validates.
- Name
account_active- Type
- boolean
- Description
falsesimulates a lapsed subscription (deny reasonaccount_inactive).
Returns the updated row (200), or 404 for an unknown jti.
The tabs on the right walk each deny path end to end: flip the switch,
then watch the device call fail. A revoked, suspended, or disabled token
— or a lapsed account, or an exhausted quota — is denied at verify AND at
check-in, so an unentitled device never wipes. A cross-tenant probe
(right token, wrong account_id) is indistinguishable from a missing
token: 404 not_found.
Deny paths
curl -X PATCH https://api.averase.com/sandbox/tokens/$JTI \
-H "Content-Type: application/json" \
-d '{"status": "revoked"}'
curl https://api.averase.com/internal/tokens/verify \
-H "Content-Type: application/json" \
-d "{\"jti\": \"$JTI\"}"
# → 403
# {
# "allow": false,
# "status": "revoked",
# "reason": "revoked",
# "quota_remaining": 3
# }
Inspect assets
The sandbox's stand-in for the Inventory page: every asset upserted by
POST /internal/assets, one row per physical machine per account, keyed
by the machine's hardware_uid (system UUID + serial). Each row carries
the full fingerprint document, any operator notes, the latest
test_results snapshot, and the first_seen_at / last_booted_at
timestamps.
Request
curl https://api.averase.com/sandbox/assets
Response (200)
[
{
"prefix_id": "asset_01J4QZ8T2M9V6XW3YB5NC7DEFG",
"account_id": "acct_demo",
"hardware_uid": "4c4c4544-0034-5a10-8052-b9c04f4b5933/74HRKY3",
"fingerprint": { "system": { "vendor": "Dell Inc." } },
"notes": null,
"test_results": null,
"first_seen_at": "2026-08-12T17:01:12Z",
"last_booted_at": "2026-08-12T17:01:12Z"
}
]
Inspect wipe results
The stand-in for the Wipe reports page: every row persisted by
POST /internal/wipe_results — the drive and wipe fields as posted, the
job_id (the idempotency key: one row per job), and the
certificate_status (pending, generating, complete, or failed).
Request
curl https://api.averase.com/sandbox/wipe_results
Response (200)
[
{
"prefix_id": "wr_01J4R2M8K3P7QS5TV9WX6YZABC",
"job_id": "0e6f4a8c-2d1b-4e5f-9a7c-8b6d5e4f3a2b",
"drive_serial": "S64FNE2R504071",
"drive_model": "SAMSUNG MZVL2512HDJD-00BL2",
"drive_capacity_gb": 512,
"wipe_method": "NIST 800-88 R2 Purge",
"wipe_status": "completed",
"certificate_status": "complete"
// …
}
]
Inspect certificates
Every certificate issued, joined with its wipe result: prefix_id,
reference (the CW-… string), account_name, signed_at,
tamper_valid, wipe_result_id, drive_serial, and drive_model.
Request
curl https://api.averase.com/sandbox/certificates
Response (200)
[
{
"prefix_id": "cert_01J4R2N9X0WQ4T8V2Y6Z1ABCDE",
"reference": "CW-000001",
"account_name": "Test Co",
"signed_at": "2026-08-12T17:41:08Z",
"tamper_valid": true,
"wipe_result_id": "wr_01J4R2M8K3P7QS5TV9WX6YZABC",
"drive_serial": "S64FNE2R504071",
"drive_model": "SAMSUNG MZVL2512HDJD-00BL2"
}
]
Verify a certificate
The public certificate check, exactly as in production: no
authentication, addressed by the certificate's cert_… id. valid
reflects the stored tamper check.
Two shapes come back: 200 with the certificate's details when it exists,
or 404 {"valid": false, "error": "not_found"} when it doesn't.
Naming gotcha: here certificate_number is the CW-… string. In
the certificate_generated webhook payload, certificate_number is
the integer and reference carries the string.
Request
curl https://api.averase.com/certificates/cert_01J4R2N9X0WQ4T8V2Y6Z1ABCDE/verify
Response (200)
{
"valid": true,
"certificate_number": "CW-000001",
"account": "Test Co",
"drive_serial": "S64FNE2R504071",
"drive_model": "SAMSUNG MZVL2512HDJD-00BL2",
"drive_capacity_gb": 512,
"wipe_method": "NIST 800-88 R2 Purge",
"passes_completed": null,
"completed_at": "2026-08-12T17:40:58Z",
"signed_at": "2026-08-12T17:41:08Z"
}
Proxy a live request
A GET-only proxy to the real web application API — the backend the
/live console runs on. Useful for comparing sandbox rows against real
account data without leaving the sandbox.
Required attributes
- Name
base_url- Type
- string
- Description
The real web application's base URL.
- Name
api_key- Type
- string
- Description
API key to send as the bearer token.
- Name
path- Type
- string
- Description
Path to GET. Must start with
/api/— anything else is rejected.
Optional attributes
- Name
query- Type
- object
- Description
Query parameters to append.
Errors: 422 for a bad base_url or a path outside /api/…; 502 when
the target is unreachable.
Request
curl https://api.averase.com/sandbox/live_request \
-H "Content-Type: application/json" \
-d '{
"base_url": "https://averase.com",
"api_key": "'"$API_KEY"'",
"path": "/api/v1/me"
}'
Response (200)
{
"status": 200,
"ok": true,
"body": {
// the real API's response, passed through
}
}
Decision logic
Every token decision in the sandbox runs the same ladder as the real system's check-in decision, in this exact order — order matters, because the staff kill switch outranks everything else:
if (status === "disabled") return "disabled"; // order matters — staff kill outranks all
if (status === "revoked") return "revoked";
if (status === "suspended") return "suspended";
if (!account_active) return "account_inactive";
if (quota_limit !== null && use_count >= quota_limit) return "quota_exceeded";
return "ok";
A missing token — or a supplied account_id that doesn't match the token's — short-circuits to not_found before the ladder runs. quota_remaining is null when unlimited, otherwise max(quota_limit - use_count, 0).
| Reason | HTTP | Returned when |
|---|---|---|
disabled | 403 | Staff kill switch — outranks every other state |
revoked | 403 | Token status is revoked |
suspended | 403 | Token status is suspended |
account_inactive | 403 | account_active is false (lapsed subscription) |
quota_exceeded | 403 | quota_limit set and use_count has reached it |
not_found | 404 | Unknown jti, or account_id supplied and mismatched |
Only the two token endpoints — POST /internal/tokens/verify and
POST /internal/tokens/validate — run the full ladder. The other mirror
endpoints (/internal/assets, /internal/wipe_results,
/internal/chassis_maps) check only that status === "active": a
non-active token gets 403 {"error":"token revoked"} (or suspended /
disabled), and an unknown one gets 404 {"error":"unknown token"}.
Two side effects to know when testing: a successful verify bumps only last_seen_at (a heartbeat — booting is unmetered), while a successful validate atomically increments use_count and reports quota_remaining after the increment. Denials also fire the wipe_session_blocked webhook — phase verify or checkin depending on which endpoint denied — which is how you test blocked-session notifications; see Webhook testing.