API · v1

HTTP API reference

Base URL: https://api.superdeduper.io. Engine and any integration uses the endpoints below. Admin endpoints under /api/v1/admin exist but are gated by Cloudflare Access + email allowlist and not documented here.

Authentication

Most write endpoints require an HMAC signature:

X-Sd-Signature: hex(hmac_sha256(install_key, raw_request_body))

The HMAC key is returned by POST /api/v1/register; store it securely client-side. The request body's top-level install_id field is what the server uses to look up the key — every signed request carries it.

For GET endpoints that sign (e.g. /api/v1/ranks), the canonical input string is documented per-endpoint.

Health & schema

Open endpoints — no auth, useful for sanity probes.

GET /healthz Public

Liveness probe.

Response body
{ "ok": true, "ts": 1779683806395 }
GET /api/v1/submit/schema.json Public

JSON Schema for /api/v1/submit's request body. Versioned + public per threat model §5.

Response body
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://api.superdeduper.io/api/v1/submit/schema.json", ... }

Install lifecycle

Anonymous install identity. Install gets a UUID + per-install HMAC key on register; all subsequent writes sign with that key.

POST /api/v1/register Public

Register a new install. Returns the install_id + HMAC key. Idempotent within the same install_id.

Request body
{
  "client_version": "0.1.9",
  "registration_proof": { "kind": "captcha", "token": "<turnstile token>" }
}
Response body
{
  "install_id": "uuid",
  "install_key_hex": "<32-byte hex>",
  "registered_at": "ISO-8601",
  "submit_endpoint": "https://api.superdeduper.io/api/v1/submit",
  "achievements_unlocked": [{ "id": "founder", "name": "Founder", "tier": "high" }]
}

Scan submission

Submit completed scans for the leaderboard + achievement evaluation.

POST /api/v1/submit HMAC

Submit a completed scan. Full payload schema lives at /api/v1/submit/schema.json. Server runs sanity checks + suspicion scoring + grants achievements before responding.

Request body
{
  "schema_version": "v1",
  "install_id": "uuid",
  "client_version": "0.1.9",
  "timestamp": "ISO-8601",
  "hardware": { "cpu_model_string": "...", "cpu_cores": 16, ... },
  "run_shape": { "wall_clock_seconds": 30, "bytes_scanned": 100000000000, ... },
  "result_summary": {
    "duplicate_groups": 10,
    "duplicate_bytes_reclaimable": 1000000000,
    "actions_taken_summary": {
      "deleted_to_recycle_bytes": 0,
      "deleted_permanently_bytes": 0,
      "hardlink_replaced_bytes": 0
    }
  }
}
Response body
{
  "accepted": true,
  "submission_id": "uuid",
  "received_at": "ISO-8601",
  "sanity": { "status": "passed" | "warned" | "rejected", "notes": [...] },
  "achievements_unlocked": [...],
  "current_ranks": { "throughput": {...}, "lifetime-reclaimed": {...} }
}
POST /api/v1/submit/review HMAC

User-flagged failed submissions — when sanity rejects a scan but the user believes it's legitimate, the client can opt in to a manual review queue.

GET /api/v1/ranks HMAC

Post-submit rank polling for GUI toast. HMAC signed over canonical "${install_id}|${submission_id}".

Query params: install_id + submission_id. Returns own rank per category — does NOT apply K-anonymity (your own rank is yours to know).

OAuth account linking

Engine drives the OAuth flow client-side (browser to provider, callback to engine's loopback HTTP server). Engine then POSTs the code + redirect_uri to the exchange endpoint below. Server returns access_token + account identity.

POST /api/v1/account/oauth/google Public

Exchange a Google OAuth authorization code for an account session.

Request body
{
  "install_id": "uuid",
  "code": "<google auth code>",
  "redirect_uri": "http://localhost:53000/oauth-callback",
  "code_verifier": "<43-char base64url PKCE verifier>"
}
Response body
{
  "access_token": "...",
  "refresh_token": "...",
  "expires_in": 3600,
  "display_name": "...",
  "account_id": "uuid"
}
POST /api/v1/account/oauth/discord Public

Exchange a Discord OAuth authorization code for an account session.

Request body
{
  "install_id": "uuid",
  "code": "<discord auth code>",
  "redirect_uri": "http://localhost:53000/oauth-callback"
}
POST /api/v1/account/me/delete HMAC

Self-delete the authenticated account + all account_installs links. Install rows + submissions stay (they become anonymous again).

Install management (v1.1)

Manage your machines after OAuth-linking. All endpoints currently restricted to self-operations: the path's {install_id} must equal the HMAC-verified install_id. Cross-install (manage another machine from current sign-in) is v1.2 work.

PATCH /api/v1/account/installs/{install_id} HMAC (self only)

Set or clear the user-friendly nickname for an install. Max 80 chars; empty string clears.

Request body
{ "install_id": "uuid", "nickname": "Work Laptop" }
Response body
{ "install_id": "uuid", "nickname": "Work Laptop" }
POST /api/v1/account/installs/{install_id}/archive HMAC (self only)

Soft-delete: marks the install archived (hidden from management lists, anonymized in display). Achievement data still flows.

Request body
{ "install_id": "uuid" }
POST /api/v1/account/installs/{install_id}/unarchive HMAC (self only)

Reverse a prior archive.

DELETE /api/v1/account/installs/{install_id}/purge HMAC (self only)

Hard-delete: all achievement_grants for the install + the account_installs link + the install nickname. Idempotent — re-purge returns 200 with zero counts, not 404.

Response body
{ "install_id": "uuid", "deleted_grants": 7, "deleted_account_install": true }

Reads

Public reads — no auth, K-anonymity applied where applicable.

GET /api/v1/leaderboards/{category}/{bracket} Public

Public leaderboard for a (category, bracket) tuple. K-anonymity suppresses brackets with <5 distinct active installs in 30 days.

Categories at G1: throughput, lifetime-reclaimed. Bracket = hardware_class string like "cpu_A_storage_NVMe-Gen5_fs_NTFS".

GET /api/v1/leaderboards/daily_stats Public

Aggregate counters for the marketing page (submissions today, total installs, etc.).

GET /api/v1/profile/me HMAC

Self-profile. Same shape as /profile/{public_id} but auth-gated for the CLI to fetch without a public_id.

GET /api/v1/profile/{public_id} Public

Public profile. public_id = install_id at G1; account_id at G3 if claimed.

Response body
{
  "install_id": "uuid",
  "display_name": "Mick",
  "install": { "nickname": "Work Laptop", "archived": false, "install_id_prefix": "e1eae1fa" },
  "lifetime": { "bytes_reclaimed": 0, "total_scans": 5, "total_files_seen": 500000 },
  "recent_runs": [...],
  "hardware_history": [...],
  "achievements": [{ "id": "founder", "granted": true, "granted_at": "...", "install": {...} }, ...]
}
GET /api/v1/corpus/versions Public

List published benchmark corpus versions.

GET /api/v1/corpus/manifest/{version} Public

Manifest for a specific corpus version (file list + sizes for client-side verification).

GET /api/v1/achievements/catalog Public

Full visible achievement catalog. Engine fetches at startup to render the badge wall.

Schema + source

The /submit request schema is published as machine-readable JSON Schema at api.superdeduper.io/api/v1/submit/schema.json.

Backend source is open at github.com/mickfixesjunk/superdeduper-web — route table in api/src/handler.ts, schemas in api/src/schema/.