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 theAuthorization 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
CallPOST /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.
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 thepublic_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.
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.
Step 5 — Store encrypted data
Send the ciphertext, the plaintext checksum, and the public key toPOST /v1/secure/data.
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-serviceverify 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_environmentdefaulted toHOTwhere you intendedCOLD
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.