Skip to main content
This guide covers the encrypt-and-store approach: you encrypt recovery material — seed phrases, key shares, or backup files — client-side with an rsa4096 key, and store the ciphertext with CoinCover. We never see plaintext, and the private key needed to recover it never leaves our enclave. If instead you want CoinCover to act as the backup key in a multi-sig wallet, see Add CoinCover as a backup key.

Step 1 — Authenticate

Authenticate with a Bearer token in the Authorization header — a JWT or a long-lived API key.

Step 2 — Choose HOT or COLD

You choose the environment per key, and it’s fixed for the life of the key.

Step 3 — Assign a key

Call POST /v1/keys with key_type: "rsa4096". Only user_identifier and key_type are required; supply organisation/package only if you want to file the key against your own internal structure.
Store the key_id against your wallet record. The response also carries a signature over the public_key, signed by the generating enclave — verify it with your CoinCover verification key before relying on the key. If verification fails, treat the response as untrusted and escalate.

Optionally, bind business context into the signature

By default the signature covers only the public_key. Pass a sign_with array on the request to have the enclave also bind chosen context fields — alongside the public_key, which is always included — so a verified signature attests to which wallet, customer, or package the key was issued for, not just the key bytes. Supported fields: external_customer_id, external_package_id, pulled_by_id, pulled_by_type, user_id, key_id, key_fingerprint. To bind external_customer_id or external_package_id, include the matching organisation/package on the request. Don’t list public_key — it’s always included implicitly, and passing it is rejected. Any field you list that can’t be resolved returns a 400. key_fingerprint is the SHA-256 (hex) of the public-key bytes; pulled_by_id/pulled_by_type are the caller identity CoinCover sets from your auth token, not request fields.
When sign_with is present, the response carries two extra fields alongside signature:
signed_payload is the exact JCS-canonical JSON string the enclave signed: object keys are sorted and the public_key is always included alongside your requested fields. signed_fields echoes the fields you requested, in request order.

Verify signature against your CoinCover verification key over those exact bytes — don’t re-serialise the JSON — then confirm the bound values match the wallet you’re provisioning. A failed signature or an unexpected value means the response is untrusted: stop and escalate.
sign_with is optional and additive. Omit it and the response is unchanged — the legacy signature covers the public_key alone, verified over the UTF-8 bytes of the hex public_key string (not its decoded bytes) — so existing integrations keep working.

Step 4 — Encrypt client-side

The public key comes back hex-encoded. Convert it to PEM, then encrypt with RSA-OAEP using SHA-256 as both the hash and MGF1 hash.
The checksum must be SHA-256 of the plaintext, not the ciphertext. It’s optional on the standard endpoint, but supplying it lets CoinCover verify the recovered plaintext on retrieval — we recommend always sending it.
RSA-OAEP has a maximum message size (around 446 bytes for a 4096-bit key with SHA-256). For anything larger, encrypt the payload with a fresh symmetric key and wrap that symmetric key with the public key — or use the file endpoint.

Step 5 — Store encrypted data

Send the ciphertext, the plaintext checksum, and the public key to POST /v1/secure/data.
You get back a backup_id. Store it — it’s the handle to find this exact backup again. For binary backups — encrypted archives, wallet seed files — use POST /v1/secure/file instead. It’s multipart/form-data, expects the file to already be encrypted on your side, and returns a backup_id the same way.

Step 6 — Verify the key and backup

Confirm integrity with the self-service verify endpoints, right after assignment and storage and periodically thereafter as a drill.
verify-key returns is_key_valid; verify-backup returns is_backup_valid. Treat a false from either as a hard failure and escalate before relying on the material.

Step 7 — Test before you ship

Run through the going to production checklist before cutting production traffic to live keys. The most common issues:
  • Hex-to-PEM conversion off by one byte (verify with a known-good public key first)
  • OAEP hash and MGF1 hash mismatched (both should be SHA-256)
  • Checksum computed over ciphertext rather than plaintext
  • Sandbox base URL still set in production config
  • key_environment defaulted to HOT where you intended COLD
The testing page has the full sandbox matrix.

What’s next

Add CoinCover as a backup key

Use a secp256k1 key as the backup key in your multi-sig or MPC wallet.

API reference

Every endpoint, request, and response.