REST API
WipeCert API Documentation
Endpoints for the desktop agent and bootable ISO, public certificate verification, and outbound webhook delivery. Base URL https://wipecert.com.
Getting Started
Overview
The WipeCert API is REST + JSON. All endpoints accept and return UTF-8 JSON with Content-Type: application/json. There is no client SDK — every contract documented here is stable and can be called directly with curl, fetch, or your language's HTTP library.
Endpoint groups:
- Agent API — used by the desktop agent and bootable ISO. Authenticated by
x-api-key. - Public Verification API — used by auditors and customers to verify certificates without auth.
- Outbound Webhooks — pushed to your URL when a wipe completes or fails. HMAC-signed.
Authentication
API Keys
Generate API keys at Dashboard → Settings → API Keys. Keys begin with wc_live_ and are tied to a single organization. The plaintext value is shown once at generation; only a bcrypt hash is stored after that.
Pass the key on the x-api-key header on every Agent API call:
curl -H "x-api-key: wc_live_..." \
https://wipecert.com/api/agent/pollNever commit API keys to source control. The desktop agent stores the key in ~/.wipecert/config.json with file-system-level permissions only — treat it the same as an SSH key.
Limits
Rate Limits
Rate limits are enforced server-side per API key (or per IP for the public verify endpoint):
| Endpoint | Key | Limit | Window |
|---|---|---|---|
| GET /api/agent/poll | API key | 60 | 60 sec |
| POST /api/agent/report | API key | 240 | 60 sec |
| POST /api/wipes | User ID | 30 | 60 sec |
| GET /api/verify/[id] | Client IP | 30 | 60 sec |
Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. When exceeded, the API returns HTTP 429 with a Retry-After header.
Agent API
Poll for queued wipes
Returns wipes the operator has queued in the dashboard but not yet processed.
Headers
| Header | Required | Description |
|---|---|---|
| x-api-key | Yes | Organization API key, prefix wc_live_ |
| x-agent-version | Recommended | Semver of the agent making the request, e.g. 1.0.11 |
Response 200
{
"wipes": [
{
"id": "6919b441-ab10-4f24-997f-460a3285fa94",
"wipe_number": "WC-2026-611157",
"method": "Multi-pass Overwrite",
"standard": "NIST 800-88 Clear",
"status": "queued",
"created_at": "2026-04-26T04:26:51.200Z",
"devices": {
"id": "c82e2459-9eee-4945-a050-66abc8cfbd84",
"name": "USB Stick",
"drive_type": "nvme",
"device_type": "laptop",
"drive_serial": "",
"serial_number": "4784984",
"drive_capacity": "",
"drive_interface": ""
}
}
]
}Errors
| Status | Reason |
|---|---|
| 401 | Missing or invalid API key |
| 429 | Rate limit exceeded — back off and retry after Retry-After seconds |
Agent API
Report wipe progress and completion
Single endpoint for all lifecycle events. Discriminator is the event field:
| event | When to send |
|---|---|
| started | Immediately after the agent locks the drive identity and computes hash before |
| progress | Every 1-5% of the wipe. Fire-and-forget, no retry. |
| completed | After the final pass and hash-after / pattern verification |
| failed | If any unrecoverable error occurs |
Body — started
{
"wipeId": "6919b441-...",
"event": "started",
"data": {
"hashBefore": "<64-char hex sha256>",
"probeSeed": "<64-char hex>",
"merkleRootBefore": "<64-char hex>",
"samplesChecked": 1024,
"drive": {
"path": "/dev/sda",
"model": "Samsung 870 EVO",
"serial": "S5HXNX0...",
"capacityBytes": 512110190592,
"logicalSectorBytes": 512,
"physicalSectorBytes": 512,
"interface": "SATA",
"type": "ssd",
"supportsSanitizeCrypto": true,
"supportsSanitizeBlock": true,
"supportsSanitizeOverwrite": true,
"tcgOpal": false,
"wwn": "...",
"firmwareRev": "..."
},
"system": {
"manufacturer": "Dell Inc.",
"model": "Latitude 5540",
"serial": "JD7XYZ2",
"biosVersion": "1.21.0"
},
"agentVersion": "1.0.11",
"methodChosen": "ata_secure_erase",
"methodReason": "ATA Sanitize advertised — preferred over multipass for SATA SSD",
"availableDrives": [
{ "path": "/dev/sda", "serial": "S5HX...", "model": "Samsung 870 EVO", "capacityBytes": 512110190592, "isRemovable": false }
]
}
}Body — completed
{
"wipeId": "6919b441-...",
"event": "completed",
"data": {
"hashAfter": "<64-char hex sha256>",
"verificationPassed": true,
"merkleRootAfter": "<64-char hex>",
"samplesMatched": 1024,
"expectedPattern": "0x00",
"actualMethodUsed": "ata_secure_erase",
"methodFallbackReason": null,
"firstFailedOffset": null
}
}Body — progress
{
"wipeId": "6919b441-...",
"event": "progress",
"data": {
"percentage": 42.5,
"currentPass": 1,
"totalPasses": 3,
"bytesProcessed": 217641533440
}
}Body — failed
{
"wipeId": "6919b441-...",
"event": "failed",
"data": {
"error": "device_disconnected_mid_wipe"
}
}Response
{ "ok": true }Agent API
Download an agent binary
Streams the latest GitHub-released binary directly. Valid platform values:
| platform | Output |
|---|---|
| wipecert-agent.exe | Windows x64 (signed UAC manifest) |
| wipecert-agent-linux | Linux x64 |
| wipecert-agent-macos-arm | macOS Apple Silicon |
| wipecert-boot.iso | Bootable ISO (UEFI + Legacy BIOS) |
SHA-256 hashes for each binary are published at /api/agent/checksums (JSON). Compare the value against your downloaded file with sha256sum (Linux/macOS) or Get-FileHash (Windows).
# Verify a downloaded binary
curl -fsSL https://wipecert.com/api/agent/download?platform=wipecert-agent.exe -o wipecert-agent.exe
curl -fsSL https://wipecert.com/api/agent/checksums
# -> { "tag": "v1.0.15", "checksums": { "wipecert-agent.exe": "abcd..." } }
sha256sum wipecert-agent.exe # compare against the value abovePublic API
Verify a certificate
No auth required. Returns the signed payload, the Ed25519 detached signature, and the JWKS URL containing the public key. Auditors can re-verify offline; see /security for the procedure.
Path parameters
id — either the certificate UUID (38a067d8-1560-40e2-...) or the human certificate number (WC-CERT-2026-611157).
Response 200
{
"certificateId": "38a067d8-...",
"certificateNumber": "WC-CERT-2026-611157",
"revision": 1,
"issuedAt": "2026-04-26T08:29:00Z",
"verification": {
"status": "VALID",
"kid": "wc-2026-01",
"signedAt": "2026-04-26T09:17:47.123Z"
},
"payload": { /* canonical signed payload (RFC 8785 / JCS form) */ },
"signature": "ZFbPrQwhT8WjVUTL...",
"kid": "wc-2026-01",
"alg": "Ed25519",
"signedAt": "2026-04-26T09:17:47.123Z",
"jwksUrl": "https://wipecert.com/.well-known/jwks.json",
"summary": {
"deviceSerial": "4784984",
"driveSerial": "S5HXNX0...",
"method": "Multi-pass Overwrite"
},
"instructions": "..."
}Verification status values
| status | Meaning |
|---|---|
| VALID | Signature verified against the active JWKS key. Certificate is authentic. |
| INVALID | Signature did not verify. Document may have been tampered with. |
| LEGACY-UNSIGNED | Pre-v2 certificate without a signature (rare; only old test data) |
| UNKNOWN-KEY | Signature kid not present in JWKS (key rotation in progress?) |
Webhooks
Outbound webhooks
Configure outbound webhook endpoints at Dashboard → Settings → Webhooks. WipeCert will POST a JSON payload to your URL on the events you select. A webhook secret is generated server-side; HMAC-SHA-256 signatures are sent in X-WipeCert-Signature.
Supported events
| event | Fires when |
|---|---|
| wipe.completed | An agent reports event=completed and the certificate is generated |
| wipe.failed | An agent reports event=failed |
Headers WipeCert sends
| Header | Value |
|---|---|
| Content-Type | application/json |
| User-Agent | WipeCert-Webhooks/1.0 |
| X-WipeCert-Event | wipe.completed | wipe.failed |
| X-WipeCert-Signature | sha256=<hex digest> |
| X-WipeCert-Delivery | <webhook delivery UUID, useful for idempotency> |
Payload — wipe.completed
{
"event": "wipe.completed",
"timestamp": "2026-04-26T08:29:00.000Z",
"organizationId": "5760cd9b-3bcc-...",
"data": {
"wipeId": "6919b441-...",
"wipeNumber": "WC-2026-611157",
"method": "Multi-pass Overwrite",
"standard": "NIST 800-88 Clear",
"deviceId": "c82e2459-...",
"durationSeconds": 2225,
"certificateId": "38a067d8-...",
"certificateNumber": "WC-CERT-2026-611157"
}
}Webhooks
Verify a webhook signature
Compute HMAC-SHA-256 of the raw request body using your webhook secret, then compare against the value of X-WipeCert-Signature (after stripping the sha256= prefix). Use a constant-time comparison.
import crypto from "node:crypto";
function verify(rawBody, signatureHeader, secret) {
const provided = signatureHeader.replace(/^sha256=/, "");
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(provided, "hex"),
Buffer.from(expected, "hex")
);
}Always verify the signature before parsing the JSON body. Reject and log mismatches — a failed verification usually means a misconfigured secret on the receiving end.