{"info":{"_postman_id":"9e318fdb-1522-47b7-93da-5b913d611afe","name":"CoinCover Recover - Aave","description":"<html><head></head><body><p>This document outlines the integration process for retail wallet providers looking to offer <strong>CoinCover Recover</strong> to their end users. By leveraging the <strong>Coincover Control Orchestrator API</strong>, this integration enables secure, identity-verified backup and recovery workflows.</p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"18877662","collectionId":"9e318fdb-1522-47b7-93da-5b913d611afe","publishedId":"2sBXVkCAGf","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-01-23T16:49:58.000Z"},"item":[{"name":"1. Authentication","item":[],"id":"10d97a8a-902e-436e-8058-e5908c1bb231","description":"<p>All requests require authentication via the header. The orchestrator supports two methods:</p>\n<ul>\n<li><p><strong>API Key:</strong> <code>X-API-Key: [your_api_key]</code></p>\n</li>\n<li><p><strong>Bearer Token:</strong> <code>Authorization: Bearer [your_api_key]</code></p>\n</li>\n</ul>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Environment</th>\n<th>Base URL</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Production</strong></td>\n<td><code>https://orchestrator.control.coincover.com</code></td>\n</tr>\n<tr>\n<td><strong>Development</strong></td>\n<td><code>https://orchestrator.uat-control.coincover.com</code></td>\n</tr>\n</tbody>\n</table>\n</div>","_postman_id":"10d97a8a-902e-436e-8058-e5908c1bb231"},{"name":"2. Backup workflow","item":[],"id":"58764224-6ecf-47c0-b8fa-2efd525fe955","description":"<p>The backup phase links a user's identity with their encrypted data. While a backup can be initiated before verification is complete, a <code>verification_id</code> must be provided to associate the records.</p>\n<img src=\"https://content.pstmn.io/151c1f73-bf1f-4ecf-9326-c7827e16f163/UmV0YWlsIERvY3VtZW50YXRpb24gQmFja3VwIFdvcmtmbG93ICgyKS5wbmc=\" width=\"577\" height=\"502\" />\n\n<h3 id=\"step-1-initialise-verification\">Step 1: Initialise verification</h3>\n<p>Call <code>POST /v1/verification/start</code> to generate a <code>verification_id</code>.</p>\n<ul>\n<li><p>Pass this ID to your frontend to initialise the <strong>Persona SDK</strong>.</p>\n</li>\n<li><p>Ensure you have a callback URL configured to receive status events (e.g., <code>APPROVED</code>, <code>DECLINED</code>).</p>\n</li>\n</ul>\n<p><strong>Request Schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"user_identifier\": \"string (required)\"  \n}  \n\n</code></pre>\n<p><strong>Response Schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"verification_id\": \"string (Persona inquiry ID)\"  \n}  \n\n</code></pre>\n<hr />\n<h4 id=\"simulation--testing\">Simulation &amp; Testing</h4>\n<p>In non-production environments, you can use the <code>x-override-status</code> header to bypass manual verification steps and trigger specific webhook events or state change</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th><strong>Name</strong></th>\n<th><strong>Type</strong></th>\n<th><strong>Description</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>x-override-status</code></td>\n<td><code>string</code></td>\n<td><strong>Optional.</strong> Simulate a Persona outcome. (Sandbox only)</td>\n</tr>\n</tbody>\n</table>\n</div><p><strong>Accepted</strong> <strong><code>x-override-status</code></strong> <strong>values:</strong></p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th><strong>Value</strong></th>\n<th><strong>Description</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>start_inquiry</code></td>\n<td>Transitions inquiry to started state</td>\n</tr>\n<tr>\n<td><code>complete_inquiry</code></td>\n<td>Marks the inquiry as completed</td>\n</tr>\n<tr>\n<td><code>fail_inquiry</code></td>\n<td>Simulates a failed inquiry attempt</td>\n</tr>\n<tr>\n<td><code>expire_inquiry</code></td>\n<td>Simulates inquiry expiration</td>\n</tr>\n<tr>\n<td><code>mark_for_review_inquiry</code></td>\n<td>Moves inquiry to manual review status</td>\n</tr>\n<tr>\n<td><code>approve_inquiry</code></td>\n<td>Triggers an <code>APPROVED</code> outcome</td>\n</tr>\n<tr>\n<td><code>decline_inquiry</code></td>\n<td>Triggers a <code>DECLINED</code> outcome</td>\n</tr>\n<tr>\n<td><code>create_passed_verification</code></td>\n<td>Generates a successful verification record</td>\n</tr>\n<tr>\n<td><code>create_failed_verification</code></td>\n<td>Generates a failed verification record</td>\n</tr>\n</tbody>\n</table>\n</div><h3 id=\"step-2-generate-cryptographic-keys\">Step 2: Generate cryptographic keys</h3>\n<p>Call <code>POST /v1/key/generate</code> to obtain the public key required for data encryption.</p>\n<ul>\n<li><strong>Required:</strong> <code>user_identifier</code> and the <code>verification_id</code> from Step 1.</li>\n</ul>\n<p><strong>Request Schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"user_identifier\": \"string (required)\",  \n  \"verification_id\": \"string (required)\"  \n}  \n\n</code></pre>\n<p><strong>Response Schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"key_id\": \"string (UUID)\",  \n  \"public_key\": \"string (hex-encoded)\",  \n  \"signature\": \"string (base64)\"  \n}  \n\n</code></pre>\n<h3 id=\"step-3-data-encryption-process\">Step 3: Data encryption process</h3>\n<p>Before calling the store endpoint, you must encrypt the sensitive data client-side.</p>\n<ol>\n<li><p><strong>Get Public Key:</strong> Use the <code>public_key</code> returned in Step 2.</p>\n</li>\n<li><p><strong>Convert Format:</strong> Convert the hex-encoded public key to the appropriate format (PEM/DER) for your crypto library.</p>\n</li>\n<li><p><strong>Generate Checksum:</strong> Generate a <strong>SHA-256 checksum</strong> of the original, unencrypted data.</p>\n</li>\n<li><p><strong>Encrypt Data:</strong> Encrypt the data using <strong>RSA-OAEP with SHA-256 padding</strong>.</p>\n</li>\n<li><p><strong>Encode:</strong> Base64 encode the resulting ciphertext.</p>\n</li>\n</ol>\n<h3 id=\"step-4-store-encrypted-backup\">Step 4: Store encrypted backup</h3>\n<p>Call <code>POST /backup/store</code> to persist the data.</p>\n<p><strong>Request schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"user_identifier\": \"string (required)\",  \n  \"verification_id\": \"string (required)\",  \n  \"public_key\": \"string (hex-encoded, required)\",  \n  \"backup\": [  \n    {  \n      \"item_key\": \"string (required, e.g., 'seed_phrase')\",  \n      \"item_value\": \"string (base64, encrypted data)\",  \n      \"item_checksum\": \"string (SHA-256 hex of original data)\"  \n    }  \n  ],  \n  \"metadata\": {  \n    \"description\": \"string (optional)\",  \n    \"content_type\": \"string (optional)\",  \n    \"original_filename\": \"string (optional)\"  \n  }  \n}  \n\n</code></pre>\n<p><strong>Response schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"backup_type\": \"data\",  \n  \"metadata\": {  \n    \"stored_at\": \"string (ISO timestamp)\",  \n    \"backup_items_count\": \"integer\"  \n  },  \n  \"backup\": [  \n    {  \n      \"item_key\": \"string\",  \n      \"backup_id\": \"string (UUID)\",  \n      \"checksum\": \"string\",  \n      \"data_size\": \"integer\"  \n    }  \n  ]  \n}  \n\n</code></pre>\n","_postman_id":"58764224-6ecf-47c0-b8fa-2efd525fe955"},{"name":"3. Recovery workflow","item":[],"id":"0e82704b-985f-47d2-82ff-d81fc8780fd2","description":"<p>Recovery is a restricted operation. Access to stored backups is only granted upon the presentation of a successful identity verification.</p>\n<img src=\"https://content.pstmn.io/aaaaafc4-a529-47de-9d23-2df0ee0d84ac/UmV0YWlsIERvY3VtZW50YXRpb24gUmVjb3ZlcnkgV29ya2Zsb3cgKDMpLnBuZw==\" width=\"599\" height=\"719\" />\n\n<h3 id=\"step-1-re-verify-identity\">Step 1: Re-verify identity</h3>\n<p>Call <code>POST /v1/verification/start</code> to begin a new recovery-specific session. Monitor the status via the configured webhook or the <code>GET /v1/verification/status</code> endpoint.</p>\n<p><strong>Response schema (</strong><code>GET /v1/verification/status</code><strong>)</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"verification_id\": \"string\",  \n  \"status\": \"pending | approved | declined | review\",  \n  \"updated_at\": \"string (ISO timestamp)\"  \n}  \n\n</code></pre>\n<h3 id=\"step-2-recover-backup\">Step 2: Recover backup</h3>\n<p>Once the inquiry status is confirmed as <strong>APPROVED</strong>, call <code>POST /v1/backup/recover</code>.</p>\n<ul>\n<li><p><strong>GPG Key:</strong> A <code>gpg_public_key</code> must be provided in the request. The orchestrator uses this to encrypt the recovered data for transit.</p>\n</li>\n<li><p><strong>Authorisation:</strong> The orchestrator validates that the provided <code>verification_id</code> is successful and belongs to the user.</p>\n</li>\n</ul>\n<p><strong>Request schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"user_identifier\": \"string (required)\",  \n  \"verification_id\": \"string (required, must be APPROVED)\",  \n  \"gpg_public_key\": \"string (PGP public key block format)\"  \n}  \n\n</code></pre>\n<p><strong>Response schema</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{  \n  \"user_identifier\": \"string\",  \n  \"verification_id\": \"string\",  \n  \"backup\": [  \n    {  \n      \"backup_id\": \"string (UUID)\",  \n      \"item_key\": \"string (e.g., 'seed_phrase')\",  \n      \"item_value\": \"string (base64, GPG encrypted data)\",  \n      \"item_checksum\": \"string (SHA-256 hex of the original unencrypted data)\",  \n      \"metadata\": {  \n        \"recovered_at\": \"string (ISO timestamp)\"  \n      }  \n    }  \n  ]  \n}  \n\n</code></pre>\n<h3 id=\"step-3-rotation-and-re-backup\">Step 3: Rotation and re-backup</h3>\n<p>Following recovery, the new recovery code should be backed up using the <code>POST /v1/backup/store</code> endpoint to ensure the backup remains current.</p>\n<blockquote>\n<p><strong>Note:</strong> A successful verification is valid for a limited window to perform recovery and subsequent re-stores.</p>\n</blockquote>\n","_postman_id":"0e82704b-985f-47d2-82ff-d81fc8780fd2"},{"name":"4. Webhook & inquiry status","item":[],"id":"a2ea8d52-2ef5-49b9-a141-07be43f8802d","description":"<p>To provide a seamless user experience, your backend should implement a webhook listener to react to identity verification updates.</p>\n<h3 id=\"source-of-truth\">Source of truth</h3>\n<blockquote>\n<p><strong>Critical:</strong> Do not use client-side events from the Persona SDK for business logic or authorisation. SDK events are intended for UI transitions only and may not trigger if a user closes the browser prematurely. <strong>Always use webhook events for backend business logic.</strong> </p>\n</blockquote>\n<h3 id=\"webhook-signature-verification\">Webhook signature verification</h3>\n<p>All webhooks sent by the Control Orchestrator include a <code>Coincover-Signature</code> header. You <strong>must</strong> verify this signature to ensure the request originated from Coincover.</p>\n<p>The signature is an HMAC-SHA256 hash calculated as: <code>HMAC_SHA256(webhook_secret, \".\")</code>.</p>\n<p><strong>Signature Header Format:</strong><br /><code>t=1759929061,v1=bda09c34e8ee64000513f2ce47fa35e9b5376b2c691ad5e1475fc14d5804fd7f</code></p>\n<ol>\n<li><p>Extract the timestamp (<code>t=</code>) and the signature (<code>v1=</code>).</p>\n</li>\n<li><p>Concatenate the timestamp and the raw request body with a dot: <code>timestamp.body</code>.</p>\n</li>\n<li><p>Compute the HMAC-SHA256 using your shared secret.</p>\n</li>\n<li><p>Use a constant-time comparison (<code>timingSafeEqual</code>) to validate the computed hash against the <code>v1</code> value.</p>\n</li>\n</ol>\n<h3 id=\"inquiry-outcomes\">Inquiry outcomes</h3>\n<p>An inquiry transitions through several states. Recovery is only possible when the outcome is <strong>APPROVED</strong>.</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Status</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>CREATED</strong></td>\n<td>The inquiry has been initialised.</td>\n</tr>\n<tr>\n<td><strong>PENDING</strong></td>\n<td>The user is currently going through the IDV flow.</td>\n</tr>\n<tr>\n<td><strong>COMPLETED</strong></td>\n<td>The user has finished the flow, and data is being processed.</td>\n</tr>\n<tr>\n<td><strong>FAILED</strong></td>\n<td>An error occurred during the verification data collection process.</td>\n</tr>\n<tr>\n<td><strong>APPROVED</strong></td>\n<td><strong>Verification Successful.</strong> Recovery operations are now authorized.</td>\n</tr>\n<tr>\n<td><strong>DECLINED</strong></td>\n<td>Data was collected successfully, but the automated IDV checks failed.</td>\n</tr>\n<tr>\n<td><strong>NEEDS_REVIEW</strong></td>\n<td>The inquiry is flagged for manual review by a compliance officer.</td>\n</tr>\n<tr>\n<td><strong>EXPIRED</strong></td>\n<td>The inquiry session timed out before completion.</td>\n</tr>\n</tbody>\n</table>\n</div>","_postman_id":"a2ea8d52-2ef5-49b9-a141-07be43f8802d"}]}