{ "openapi": "3.0.0", "info": { "title": "Recover for Wallet Providers — Partner Key Service API", "description": "Partner Key Service for wallet providers integrating CoinCover under the hood of their platform to back up their customers' wallets. All endpoints live under /v1/partner/* and require organisation and package metadata. HOT vs COLD is selected with the key_environment parameter at assignment time, not via the URL path. Key generation and storage use AWS Nitro Enclaves for hardware-isolated cryptographic operations.", "version": "1.0.0" }, "servers": [ { "url": "https://service.uat-keys.coincover.com", "description": "Sandbox (UAT)" }, { "url": "https://service.keys.coincover.com", "description": "Production — confirm host with your account manager" } ], "security": [ { "bearerAuth": [] } ], "tags": [ { "name": "Keys", "description": "Generate, assign, and verify cryptographic keys." }, { "name": "Backups", "description": "Store and verify encrypted backup data and files." } ], "paths": { "/v1/partner/keys": { "post": { "summary": "Partner - Assign a new key with environment specification", "description": "Creates a new key pair with the enclave, shards the private key, and securely stores the encrypted key shards. Includes key environment specification: HOT (online generation/storage) or COLD (offline generation/storage). Partner-only endpoint.", "operationId": "partnerAssignKey", "tags": ["Keys"], "parameters": [ { "name": "Authorization", "in": "header", "description": "Bearer token for authentication. Can be either a JWT token or API key.", "required": true, "schema": { "$ref": "#/components/schemas/Authorization" } } ], "requestBody": { "description": "Key assignment details with key environment specification", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PartnerAssignKeyRequest" } } } }, "responses": { "200": { "description": "Key successfully assigned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PartnerAssignKeyResponse" } } } }, "400": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Token lacks partner-endpoint scope", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Key already assigned for this user and organisation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/partner/secure/data": { "post": { "summary": "Partner - Store secure encrypted data", "description": "Stores base64-encoded encrypted data with checksum validation and padding type specification. The data is verified using the enclave before storage. Partner-only endpoint. The checksum is required and must be the SHA-256 of the plaintext.", "operationId": "partnerStoreSecureData", "tags": ["Backups"], "parameters": [ { "name": "Authorization", "in": "header", "description": "Bearer token for authentication. Can be either a JWT token or API key.", "required": true, "schema": { "$ref": "#/components/schemas/Authorization" } } ], "requestBody": { "description": "Secure data and metadata", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PartnerSecureDataRequest" } } } }, "responses": { "200": { "description": "Secure data successfully stored", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SecureDataResponse" } } } }, "400": { "description": "Invalid request parameters or verification failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Key or user not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/partner/secure/file": { "post": { "summary": "Partner - Store secure encrypted files", "description": "Stores encrypted files securely. The key environment is determined from the provided key. The file must already be encrypted on the partner side; CoinCover applies no additional server-side encryption. Maximum payload 10MB. Partner-only endpoint.", "operationId": "partnerStoreSecureFile", "tags": ["Backups"], "parameters": [ { "name": "Authorization", "in": "header", "description": "Bearer token for authentication. Can be either a JWT token or API key.", "required": true, "schema": { "$ref": "#/components/schemas/Authorization" } } ], "requestBody": { "description": "Secure file and metadata", "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PartnerSecureFileRequest" } } } }, "responses": { "200": { "description": "Secure file successfully stored", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SecureFileResponse" } } } }, "400": { "description": "Invalid request parameters or verification failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "413": { "description": "Payload too large (over 10MB)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "415": { "description": "Unsupported media type", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } } }, "components": { "schemas": { "PartnerAssignKeyRequest": { "type": "object", "required": [ "user_identifier", "key_type", "key_environment", "organisation", "package" ], "properties": { "user_identifier": { "type": "string", "description": "Unique identifier of the user" }, "key_type": { "type": "string", "enum": [ "rsa4096", "secp256k1", "ed25519" ], "description": "Type of cryptographic key to generate. rsa4096 for encryption of recovery material; secp256k1 or ed25519 for blockchain backup signers." }, "key_environment": { "type": "string", "enum": [ "HOT", "COLD" ], "description": "Key environment specification - HOT (online generation) or COLD (offline generation)" }, "organisation": { "type": "object", "description": "Organisation metadata", "properties": { "customer_id": { "type": "string", "description": "Unique identifier for the organisation" }, "customer_name": { "type": "string", "description": "Name of the organisation customer" } } }, "package": { "type": "object", "description": "Package metadata", "properties": { "package_id": { "type": "string", "description": "External package ID assigned to the key" }, "package_name": { "type": "string", "description": "Name of the package" } } }, "sign_with": { "type": "array", "description": "Optional list of context fields to bind into the enclave signature. When present, the enclave signs a JCS-canonical (RFC 8785) JSON payload built from these fields plus the public_key (always included), returned as signed_payload, so a verified signature attests to the business context the key was issued for — not just the key bytes. Do not list public_key here; it is included implicitly.", "items": { "type": "string", "enum": [ "external_customer_id", "external_package_id", "pulled_by_id", "pulled_by_type", "user_id", "key_id", "key_fingerprint" ] } } } }, "PartnerAssignKeyResponse": { "type": "object", "properties": { "key_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the key" }, "public_key": { "type": "string", "description": "Public key in hex format" }, "signature": { "type": "string", "description": "Signature of the key in base64 format, produced by the generating enclave. Verify against CoinCover's published JWKS before encrypting recovery material. When sign_with is supplied the signature is computed over signed_payload; otherwise it covers the public key." }, "signed_payload": { "type": "string", "description": "JCS-canonical (RFC 8785) JSON string that was signed — object keys sorted, always including public_key alongside the requested context fields. Returned only when sign_with was supplied. Verify the signature over these exact bytes rather than a re-serialised copy." }, "signed_fields": { "type": "array", "description": "The sign_with field names that were included, in the order requested (public_key is not listed). Confirm this contains every field you expected before trusting the binding.", "items": { "type": "string" } }, "customer_id": { "type": "string", "nullable": true, "description": "External customer identifier (e.g. organisation customer ID)" }, "package_id": { "type": "string", "nullable": true, "description": "External package identifier (e.g. workspace/package ID)" }, "metadata": { "type": "object", "description": "Optional metadata describing the associated organisation and package", "properties": { "customer_name": { "type": "string", "nullable": true, "description": "Human-readable name of the customer or organisation" }, "package_name": { "type": "string", "nullable": true, "description": "Human-readable name of the associated package or workspace" } } } } }, "PartnerSecureDataRequest": { "type": "object", "required": [ "public_key", "checksum", "data" ], "properties": { "public_key": { "type": "string", "pattern": "^[0-9a-fA-F]+$", "minLength": 32, "maxLength": 4096, "description": "Public key in hex format used to encrypt the data" }, "checksum": { "type": "string", "pattern": "^[a-fA-F0-9]{64}$", "description": "SHA-256 checksum of the original plaintext for verification (required for partner endpoint)" }, "data": { "type": "string", "format": "base64", "description": "Base64-encoded encrypted data" }, "padding_type": { "type": "string", "enum": [ "OAEP" ], "default": "OAEP", "description": "Padding type used for encryption" }, "metadata": { "type": "object", "description": "Optional metadata about the data", "properties": { "description": { "type": "string", "description": "Human-readable description of the data" }, "content_type": { "type": "string", "description": "MIME type of the original data" }, "original_filename": { "type": "string", "description": "Original filename of the data" } } } } }, "SecureDataResponse": { "type": "object", "properties": { "backup_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the stored backup" }, "checksum": { "type": "string", "description": "SHA-256 checksum that was verified" }, "data_size": { "type": "integer", "description": "Size of the encrypted data in bytes" }, "backup_type": { "type": "string", "enum": [ "data" ], "description": "Type of backup (always 'data' for secure-data endpoint)" }, "metadata": { "type": "object", "description": "Combined metadata including original metadata" } } }, "PartnerSecureFileRequest": { "type": "object", "required": [ "public_key", "file" ], "properties": { "public_key": { "type": "string", "pattern": "^[0-9a-fA-F]+$", "minLength": 32, "maxLength": 4096, "description": "Public key in hex format used to encrypt the file" }, "file": { "type": "string", "format": "binary", "description": "Encrypted file to upload (e.g. a ZIP archive)" }, "metadata": { "type": "object", "description": "Optional metadata about the file", "properties": { "description": { "type": "string", "description": "Human-readable description of the file" }, "content_type": { "type": "string", "description": "MIME type of the original file" }, "original_filename": { "type": "string", "description": "Original filename of the file" } } } } }, "SecureFileResponse": { "type": "object", "properties": { "backup_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the stored file" }, "backup_type": { "type": "string", "enum": [ "file" ], "description": "Type of backup (always 'file' for secure-file endpoint)" }, "metadata": { "type": "object", "description": "Combined metadata including original metadata" } } }, "ErrorResponse": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "error": { "type": "string", "description": "Error type" } } }, "Authorization": { "type": "string" } }, "securitySchemes": { "bearerAuth": { "description": "Bearer token authentication. Can be either a JWT token or API key.", "type": "http", "scheme": "bearer" } } } }