QopanzaDocs

Developer guide

Build with the Qopanza API

Post-quantum encryption, signatures, secrets and cryptography discovery over plain JSON and HTTPS. Call it from any language, or use one of nine SDKs that wrap it.

Base URL
https://api.qopanza.com
Authentication
X-API-Key header
Format
JSON in and out; binary as base64
Algorithms
ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205)

Two ways to use Qopanza

What you can do with the API

  • Encrypt and decrypt data with ML-KEM and AES-256-GCM, without managing keys — Encrypt and decrypt.
  • Sign and verify releases, documents and tokens with ML-DSA or SLH-DSA — Sign and verify.
  • Store secrets and issue certificates under quantum-safe keys, with versions, rollback and an audit trail — Endpoint reference.
  • Find cryptography that will break in code, repositories, TLS endpoints and cloud accounts, and find secrets your app leaks today — Scanning.
  • Gate builds on scan results from any CI system — Command line and CI.

Using the dashboard

Everything the API does is also in the dashboard at qopanza.com, and none of it needs code. The Getting started page in the sidebar walks through these steps in order.

  1. Scan your app

    Open App Security Scan and paste the address of anything you have deployed, or paste a file. It reads the JavaScript your site serves to every visitor and reports leaked API keys, database credentials, published source maps and missing security headers — with the fix for each. You can try it without an account on the free scan.

  2. See what a quantum computer breaks

    Open PQC Scanner and choose what to scan: a file of code, a live TLS endpoint, a Git repository, or a cloud account. It finds RSA, elliptic-curve and other quantum-vulnerable algorithms. Inventory lists every algorithm found across all scans, and Posture turns it into a risk score with what to fix first. An app with no cryptography of its own usually has an empty result here, and that is fine.

  3. Keep scanning and prove it

    Connect GitHub from the PQC Scanner so repositories are rescanned automatically, and turn on a public verification page — a link for your site's footer that shows what was scanned and when. Notifications sends scan results, keys about to expire and certificate changes to a Slack channel or to your own webhook, signed with HMAC-SHA256.

  4. Plan the migration and report on it

    Migration builds a plan from your inventory, ordered by exposure, and tracks it to done. Compliance scores the account against the controls Qopanza can evidence, and the inventory exports as a CycloneDX CBOM for an auditor.

  5. Hand it to your developers

    API Keys issues keys for your services, and SDKs shows the install line and a working snippet for each language with your new key filled in. Team invites colleagues, and Usage shows your plan's limits.

Quickstart

Three steps from nothing to a working round trip. No key management needed.

  1. Get an API key

    Create an account. Your first key is shown on the next screen, and you can make more under API Keys in the dashboard. A key is shown once and stored hashed, so copy it somewhere safe then.

    export QOPANZA_API_KEY="qsk_…"   # the key you were shown
  2. Encrypt something

    Binary travels as base64. With no key_id, Qopanza creates a managed ML-KEM key for your account on first use and reuses it after.

    curl -s https://api.qopanza.com/v1/encrypt \
      -H "X-API-Key: $QOPANZA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"plaintext": "Y2FyZCBlbmRpbmcgNDI0Mg=="}'

    Y2FyZCBlbmRpbmcgNDI0Mg== is card ending 4242 in base64. The response:

    {
      "key_id": "8f14e45f-…",
      "ciphertext_kem": "…",
      "ciphertext_payload": "…",
      "nonce": "…",
      "backend": "liboqs",
      "used_managed_key": true
    }
  3. Decrypt it

    Send back key_id, ciphertext_kem, ciphertext_payload and nonce. Store all four together; losing any one of them makes the data unrecoverable.

    curl -s https://api.qopanza.com/v1/decrypt \
      -H "X-API-Key: $QOPANZA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"key_id": "…", "ciphertext_kem": "…", "ciphertext_payload": "…", "nonce": "…"}'
    
    # → {"plaintext": "Y2FyZCBlbmRpbmcgNDI0Mg=="}

Authentication

Send your API key in the X-API-Key header on every request. Keys start with qsk_ and belong to one account; everything a key creates is scoped to that account.

curl https://api.qopanza.com/v1/security/posture -H "X-API-Key: $QOPANZA_API_KEY"
  • Keep keys on the server. A key in browser JavaScript or a mobile app can be read by anyone who opens it — it is exactly what Qopanza's own app scan reports. Read it from an environment variable or your secret manager.
  • One key per service. Create, rotate and revoke keys under API Keys in the dashboard. Rotating issues a new key and retires the old one, so a leaked key can be replaced without touching the others.
  • Restrict by IP. A key can carry an IP allowlist; requests from anywhere else get 403.
  • Dashboard-only operations — managing users, API keys, webhooks, billing and usage — take a signed-in session rather than an API key, so a key that leaks cannot mint more keys.

Keys and algorithms

You rarely need to pick an algorithm. Create a key by purpose and a security level, and the policy engine chooses the NIST algorithm and parameter set.

purposeUse it forAlgorithm
kemEncrypting data: /encrypt, /decrypt, secretsML-KEM (FIPS 203)
hybrid_kemKey exchange that stays safe if either half is brokenML-KEM combined with X25519
signatureSigning releases, documents, tokensML-DSA-65 by default, or SLH-DSA-SHA2-128S
curl -s https://api.qopanza.com/v1/keys \
  -H "X-API-Key: $QOPANZA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"purpose": "signature", "label": "releases", "security_level": "high"}'
  • security_level is standard (the default), high or maximum. The response's policy_rationale says why that algorithm was chosen.
  • expires_in_days and rotation_period_days set a lifetime and automatic rotation. Rotation adds a version; data encrypted under older versions still decrypts.
  • Zero-knowledge keys: generate the key pair on your own machine and register only the public half with POST /v1/keys/register. Qopanza inventories and polices it but can never decrypt with it — so /encrypt, /decrypt and /sign return 409 for it by design. There is no escrow: lose the private key and the data is gone.

Encrypt and decrypt

POST /v1/encrypt takes plaintext (base64) and an optional key_id. Each call runs an ML-KEM encapsulation to get a fresh 256-bit shared secret, and encrypts your data under it with AES-256-GCM — so the plaintext can be any size, and no two messages share a data key.

Field returnedWhat it is
key_idThe key used. Required to decrypt.
ciphertext_kemThe ML-KEM ciphertext the shared secret is recovered from.
ciphertext_payloadYour data, AES-256-GCM encrypted.
nonceThe AES-GCM nonce.
used_managed_keyTrue when no key_id was sent.

Store the four required fields together, for example as one JSON column. POST /v1/decrypt takes them back and returns plaintext in base64.

Sign and verify

Create a key with purpose: "signature", then sign a base64 message. Verification returns {"valid": true} or false; a bad signature is not an error.

curl -s https://api.qopanza.com/v1/sign \
  -H "X-API-Key: $QOPANZA_API_KEY" -H "Content-Type: application/json" \
  -d '{"key_id": "…", "message": "cmVsZWFzZS0xLjQuMg=="}'
# → {"key_id": "…", "signature": "…", "backend": "liboqs"}

curl -s https://api.qopanza.com/v1/verify \
  -H "X-API-Key: $QOPANZA_API_KEY" -H "Content-Type: application/json" \
  -d '{"key_id": "…", "message": "cmVsZWFzZS0xLjQuMg==", "signature": "…"}'
# → {"valid": true}

Scanning

There are two scanners, answering different questions.

Cryptography discovery

Which of my cryptography does a quantum computer break? Scans code, repositories, live TLS endpoints and cloud accounts, and builds an inventory you can export as a CycloneDX CBOM.

curl -s https://api.qopanza.com/v1/scan/tls \
  -H "X-API-Key: $QOPANZA_API_KEY" -H "Content-Type: application/json" \
  -d '{"host": "api.example.com", "port": 443}'

App security

What can a stranger read out of my app today? Reads the JavaScript, source maps and headers a deployed site sends to every visitor and reports leaked keys and exposed data.

curl -s https://api.qopanza.com/v1/app-scan/url \
  -H "X-API-Key: $QOPANZA_API_KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://app.example.com"}'

What comes back

A discovery scan (from /v1/scan/tls):

{
  "id": "3c9f…",
  "source_type": "tls",
  "target": "api.example.com:443",
  "status": "completed",
  "assets_found": 4,
  "quantum_vulnerable": 3,
  "quantum_safe": 1,
  "risk_score": 72,
  "created_at": "2026-09-25T09:14:02Z",
  "completed_at": "2026-09-25T09:14:05Z"
}

An app scan (from /v1/app-scan/url), shortened:

{
  "target": "https://app.example.com",
  "reachable": true,
  "score": 41,
  "critical_count": 1,
  "high_count": 2,
  "summary": "A service-role key is readable in your JavaScript…",
  "findings": [ … ],
  "fix_available": true,
  "detected_host": "vercel"
}

A discovery scan returns a run with counts — assets_found, quantum_vulnerable, quantum_safe, risk_score. Fetch the individual findings with GET /v1/scans/{scan_id}/assets, and the account-wide picture with GET /v1/security/posture. An app scan returns its findings directly, with every secret already redacted.

Endpoint reference

The operations you call with an API key, grouped by job. The walkthroughs above show the request and response for the common ones. For a field that is not covered here, email support@qopanza.com.

Encryption

POST/v1/encryptEncrypt base64 data. Omit key_id to use your account's managed key.
POST/v1/decryptDecrypt. Send back all four fields /encrypt returned.

Signatures

POST/v1/signSign a base64 message with a signature key.
POST/v1/verifyCheck a signature. Returns {"valid": true | false}.

Keys

POST/v1/keysCreate a key: purpose kem, signature or hybrid_kem.
POST/v1/keys/registerRegister the public half of a key you generated yourself (zero-knowledge).
GET/v1/keys/{key_id}Read a key's algorithm, public key and expiry.
POST/v1/keys/{key_id}/rotateCreate the next version. Older versions still decrypt.
GET/v1/keys/{key_id}/versionsList every version of a key.
PATCH/v1/keys/{key_id}/rotation-policySet or clear automatic rotation.
POST/v1/keys/{key_id}/revokeRevoke a key version.
POST/v1/kem/encapsulateRaw ML-KEM: derive a shared secret and its ciphertext.
POST/v1/kem/decapsulateRecover the shared secret from a KEM ciphertext.

Secrets

PUT/v1/secrets/{name}Store a new version of a secret, encrypted with a quantum-safe key.
POST/v1/secrets/{name}/revealRead a secret's value. Every read is audited.
GET/v1/secretsList secrets (names and metadata, never values).
GET/v1/secrets/{name}/versionsList a secret's versions.
POST/v1/secrets/{name}/rollbackMake an earlier version current again.
DELETE/v1/secrets/{name}Destroy a secret and every version of it.

Certificates

POST/v1/certificates/caCreate an ML-DSA certificate authority for internal mTLS. Not publicly trusted — browsers reject ML-DSA chains.
POST/v1/certificatesIssue a certificate from one of your CAs.
GET/v1/certificatesList issued certificates.
GET/v1/certificates/{certificate_id}/verifyVerify a certificate's chain and status.
POST/v1/certificates/{certificate_id}/revokeRevoke a certificate.

Cryptography discovery

Find the cryptography a quantum computer breaks — in code, on live endpoints and in cloud accounts.

POST/v1/scan/codeScan source or manifest text you send.
POST/v1/scan/tlsProbe a live TLS endpoint and record what it negotiates.
POST/v1/scan/repositoryClone a repository and scan it.
POST/v1/scan/cloudInventory keys and certificates in a cloud account.
GET/v1/scans/{scan_id}/assetsThe algorithms one scan found, with file and line.
GET/v1/inventoryEverything found across all scans.
GET/v1/inventory/cbomThe inventory as a CycloneDX 1.6 CBOM.
GET/v1/security/postureQuantum risk score and what to fix first.

App security

Find secrets and misconfigurations a stranger can read out of a deployed app.

POST/v1/app-scan/urlScan a live site from the outside: bundles, source maps, headers.
POST/v1/app-scan/codeScan code you send for leaked keys and credentials.
POST/v1/app-scan/fixGenerate fixes for the findings of a scan.

Migration, compliance and audit

POST/v1/migration/plansBuild a migration plan from your inventory.
GET/v1/migration/progressHow far the migration has got.
GET/v1/compliance/reportScores your account against the compliance controls Qopanza can evidence.
GET/v1/audit-logThe tamper-evident log of every key operation.
GET/v1/audit-log/verifyVerify the log's hash chain has not been altered.

No API key needed

POST/v1/app-scan/publicThe free scan on the home page. Rate-limited per visitor.
GET/v1/sdksEvery SDK, with the install command that works today.
GET/v1/healthLiveness check.

Errors and rate limits

Errors are JSON with a detail field that says what went wrong in plain words. Validation errors (422) carry a list naming each bad field.

{"detail": "Rate limit exceeded: 60 requests/minute for the 'free' plan. Try again in 23s."}
StatusMeaning
400The request is valid but cannot be carried out — for example, decryption failed because the ciphertext was altered or belongs to another key.
401No X-API-Key header, or the key is wrong, revoked or expired.
402The feature is not included in your plan. The detail names the plan that has it.
403The key is valid but not allowed here — for example, the request came from an IP outside the key's allowlist.
404No such key, scan, secret or certificate on your account.
409The key cannot be used this way: it has been revoked, or it is a zero-knowledge key whose private half the server never has.
422A field is missing or the wrong type. Binary fields that are not valid base64 land here.
429Rate limit reached. Wait for the number of seconds in Retry-After.
503A dependency is briefly unavailable. Safe to retry with backoff.

Rate limits are per account, per minute, and rise with your plan. A limited request gets 429 with two headers: Retry-After (seconds until the window resets) and X-RateLimit-Limit (your per-minute allowance). Wait for Retry-After rather than retrying at once. The Usage page in the dashboard shows your limit and recent traffic.

SDKs

Each SDK is a thin client over the same API. Pick a language — your choice is remembered on this device.

Install

pip install qopanza

Create a client

Create it once and reuse it. The key is read from QOPANZA_API_KEY, never written into source.

import os
from qopanza import QopanzaClient

client = QopanzaClient(
    base_url="https://api.qopanza.com",
    api_key=os.environ["QOPANZA_API_KEY"],
)

Encrypt and decrypt

# No key management: a managed key is created for your account on
# first use and reused after that.
sealed = client.encrypt(plaintext=b"card ending 4242")

# Store every field of `sealed` — decrypt needs all four.
plaintext = client.decrypt(**sealed)

# Or encrypt to a key you created and named yourself.
key = client.create_key(purpose="kem", label="orders")
sealed = client.encrypt(key_id=key["id"], plaintext=b"card ending 4242")

Sign and verify

sig_key = client.create_key(purpose="signature", label="releases")

signed = client.sign(key_id=sig_key["id"], message=b"release-1.4.2.tar.gz sha256:…")
ok = client.verify(
    key_id=sig_key["id"],
    message=b"release-1.4.2.tar.gz sha256:…",
    signature=signed["signature"],
)

Scan

run = client.scan_tls("api.example.com")          # a live endpoint
run = client.scan_code("payments.py", open("payments.py").read())
run = client.scan_repository("https://github.com/org/repo")

for asset in client.list_scan_assets(run["id"]):
    print(asset)

posture = client.security_posture()   # quantum risk score for the account

Handle errors

from qopanza import QopanzaAPIError

try:
    client.get_key("does-not-exist")
except QopanzaAPIError as err:
    print(err.status_code, err.detail)
  • Synchronous. Under asyncio, call it through asyncio.to_thread.
  • Installing it also installs the `qopanza` command line — see Command line and CI below.
  • `pip install 'qopanza[zk]'` adds zero-knowledge keys: the private key is generated on your machine and never uploaded.

Full reference: sdks/python/README.md in the qopanza-sdks repository ↗.

What each SDK covers

PythonTypeScriptGoRustJavaC#PHPRubyC++
Encrypt / decrypt✓✓✓✓✓✓✓✓✓
Managed key (no key_id)✓–✓✓––✓✓–
Sign / verify✓✓✓✓✓✓✓✓✓
AI analysis✓✓✓✓✓✓✓✓✓
Scanning and posture✓–✓✓––✓✓–
Secrets, certificates, migration✓––––––––
Zero-knowledge keys✓––––––––

Anything an SDK does not wrap is still one HTTPS request away — every endpoint works with any HTTP client and the X-API-Key header.

Command line and CI

Installing the Python SDK also installs the qopanza command. It reads QOPANZA_API_KEY and QOPANZA_BASE_URL, so CI can inject both without writing anything to disk.

qopanza login                        # store credentials locally
qopanza scan .                       # scan a directory
qopanza scan --tls api.example.com   # probe a live TLS endpoint
qopanza posture                      # quantum risk score
qopanza inventory                    # cryptographic inventory
qopanza key create --purpose hybrid_kem

Use it as a build gate. Add --json to any command for machine-readable output.

qopanza scan . --fail-on high

Exit codes: 0 clean, 1 the scan could not run, 2 findings at or above the threshold. The two failures are different on purpose — a scan that did not run must never pass a build.

AI coding agents (MCP)

If you build with an AI coding agent — Lovable, Replit, Claude Code, Codex, Grok Build, Cursor, Windsurf or Claude Desktop — you can give it Qopanza as a set of tools. Then you ask in plain words, "scan my app for exposed secrets and fix what you find", and the agent runs the scan, reads the findings and edits your code. No dashboard and no report to read.

This works through the Model Context Protocol (MCP), the standard way agents load extra tools. There are two ways in, with the same four tools behind both, and each needs an API key from API Keys in the dashboard:

  • Agents in the cloud (Lovable, Replit) connect to Qopanza's hosted server by URL: https://api.qopanza.com/mcp.
  • Agents on your computer (Claude Code, Codex, Grok Build, Cursor, Windsurf, Claude Desktop) run the qopanza-mcp ↗ package with npx, so there is nothing to clone or build. It needs Node 18 or newer.

Lovable

  1. Open Connectors, select +, and choose MCP server.
  2. Server name: Qopanza. Server URL: https://api.qopanza.com/mcp.
  3. For authentication choose Bearer token or API key and paste your qsk_… key.

Replit

  1. In your Repl, open Integrations, go to MCP Servers, and click + Add MCP server.
  2. Display name: Qopanza. MCP Server URL: https://api.qopanza.com/mcp.
  3. Open Advanced settings, add a header named X-API-Key with your qsk_… key as its value, then click Test & save.

Any other agent that connects to MCP servers by URL works the same way: point it at https://api.qopanza.com/mcp and send the key as X-API-Key or as Authorization: Bearer qsk_….

Claude Code

claude mcp add qopanza -e QOPANZA_API_KEY=qsk_… -- npx -y qopanza-mcp

Codex (OpenAI)

codex mcp add qopanza --env QOPANZA_API_KEY=qsk_… -- npx -y qopanza-mcp

Or add it by hand to ~/.codex/config.toml:

[mcp_servers.qopanza]
command = "npx"
args = ["-y", "qopanza-mcp"]

[mcp_servers.qopanza.env]
QOPANZA_API_KEY = "qsk_…"

Grok Build (xAI)

Add this to ~/.grok/config.toml. Grok fills in ${QOPANZA_API_KEY} from your shell when it starts, so the key itself never has to be written into the file. Then run grok mcp doctor qopanza to check the connection.

[mcp_servers.qopanza]
command = "npx"
args = ["-y", "qopanza-mcp"]
env = { QOPANZA_API_KEY = "${QOPANZA_API_KEY}" }

Cursor, Windsurf and Claude Desktop

Add this to the agent's MCP settings file — ~/.cursor/mcp.json for Cursor, ~/.codeium/windsurf/mcp_config.json for Windsurf, or claude_desktop_config.json for Claude Desktop — then restart the agent.

{
  "mcpServers": {
    "qopanza": {
      "command": "npx",
      "args": ["-y", "qopanza-mcp"],
      "env": { "QOPANZA_API_KEY": "qsk_…" }
    }
  }
}

The key stays in your agent's own settings on your machine, not in your project, so it is never committed. Create one under API Keys in the dashboard.

The tools your agent gets

ToolPlanWhat the agent can do with it
scan_app_urlFreeScan a deployed site for leaked keys, credentials and readable databases.
scan_app_codeFreeScan a file it is editing, before the change ships.
scan_cryptoFreeFind cryptography a quantum computer breaks, for code that has its own.
get_fixesApp Security and upTurn findings into exact code edits, config files and SQL policies to apply.

One thing an agent cannot do for you: rotate a leaked key. That happens in the provider's console — Stripe, Supabase, OpenAI — and every response from get_fixes lists those manual steps separately, so the agent does not report "fixed" while the old key still works.

More resources