> ## Documentation Index
> Fetch the complete documentation index at: https://developer.coincover.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration guide

> End-to-end integration of Recover for Retail.

This guide walks you through the full retail recovery integration: identity verification, key generation, client-side encryption, backup storage, and the recovery flow with GPG-encrypted return.

The backup phase links a customer's identity with their encrypted data. While a backup can be initiated before verification is complete, a `verification_id` must be provided to associate the records.

What you actually encrypt and send us depends on your architecture. For wallet providers using a split-key design (the typical retail pattern), this is a recovery-key shard, and the encrypted seed phrase itself stays in your own infrastructure. The API contract is identical — see [the overview](/retail-recovery/overview#what-we-hold-depends-on-your-architecture) for context.

<Note>
  Two things are worth knowing before you start. First, all sensitive payloads are encrypted on-device — we never see plaintext. Second, business logic must be driven by CoinCover webhooks, not by the Identity SDK's UI events. The Identity SDK events are for transitions in your wallet UI; the webhooks are your source of truth.
</Note>

## Backup workflow

### Step 1 — Initialise verification

Call `POST /v1/verification/start` to generate a `verification_id` (the inquiry ID from identity, our IDV provider). Pass that ID to your front end and use it to initialise the identity SDK. Make sure you've configured a callback URL to receive status events (`approved`, `declined`, and the rest).

**Request**

```json theme={null}
{
  "user_identifier": "string (required)"
}
```

**Response**

```json theme={null}
{
  "verification_id": "string (Identity inquiry ID)"
}
```

### Step 2 — Generate cryptographic keys

Call `POST /v1/key/generate` to obtain the public key your customer will use for client-side encryption. You'll need the `user_identifier` and the `verification_id` from Step 1.

**Request**

```json theme={null}
{
  "user_identifier": "string (required)",
  "verification_id": "string (required)"
}
```

**Response**

```json theme={null}
{
  "key_id": "string (UUID)",
  "public_key": "string (hex-encoded)",
  "signature": "string (base64)"
}
```

The key pair is generated inside a CoinCover enclave. The response includes a `signature` over the `public_key`, signed by the enclave that generated the key. During integration, CoinCover issues you a verification key — use it to verify the signature on every key-generate response. A successful verification confirms the public key really came from a legitimate CoinCover enclave; if it fails, treat the response as untrusted and escalate before encrypting any backup material.

Store the `key_id` against your customer record. You'll reference it in your audit logs and customer-support tooling.

### Step 3 — Encrypt the data on-device

Before calling the store endpoint, encrypt the sensitive data on the customer's device. This is the step that keeps plaintext out of our infrastructure — get it right and you're done.

<Steps>
  <Step title="Get the public key">
    Use the `public_key` returned in Step 2.
  </Step>

  <Step title="Convert format">
    Convert the hex-encoded public key to PEM or DER, whichever your crypto library expects.
  </Step>

  <Step title="Generate a checksum">
    Compute a SHA-256 checksum of the original, unencrypted data. You'll send this alongside the ciphertext.
  </Step>

  <Step title="Encrypt">
    Encrypt the plaintext using RSA-OAEP with SHA-256 padding.
  </Step>

  <Step title="Encode">
    Base64-encode the resulting ciphertext.
  </Step>
</Steps>

### Step 4 — Store the encrypted backup

Call `POST /v1/backup/store` to persist the data.

**Request**

```json theme={null}
{
  "user_identifier": "string (required)",
  "verification_id": "string (required)",
  "public_key": "string (hex-encoded, required)",
  "backup": [
    {
      "item_key": "string (required, e.g., 'recovery_key_shard' or 'seed_phrase')",
      "item_value": "string (base64, encrypted data)",
      "item_checksum": "string (SHA-256 hex of original data)"
    }
  ],
  "metadata": {
    "description": "string (optional)",
    "content_type": "string (optional)",
    "original_filename": "string (optional)"
  }
}
```

**Response**

```json theme={null}
{
  "backup_type": "data",
  "metadata": {
    "stored_at": "string (ISO timestamp)",
    "backup_items_count": "integer"
  },
  "backup": [
    {
      "item_key": "string",
      "backup_id": "string (UUID)",
      "checksum": "string",
      "data_size": "integer"
    }
  ]
}
```

That's the backup phase done. Your customer now has a record they can recover later.

## Recovery workflow

Recovery is a restricted operation. We only release stored backups when we've seen a successful identity verification.

### Step 1 — Re-verify identity

Call `POST /v1/verification/start` to begin a new recovery-specific session. Monitor the status via your configured webhook, or poll `GET /v1/verification/status` if you need a synchronous read.

**Response (`GET /v1/verification/status`)**

```json theme={null}
{
  "verification_id": "string",
  "status": "pending | approved | declined | review",
  "updated_at": "string (ISO timestamp)"
}
```

### Step 2 — Recover the backup

Once the inquiry status is `approved`, call `POST /v1/backup/recover`. You'll need the `verification_id` from Step 1 and a GPG public key — we use the GPG key to encrypt the recovered data on its way back to you. The orchestrator validates server-side that the verification is approved before releasing any backup material.

The endpoint returns every backup item associated with that user and verification — the response is an array. If the user only ever stored one item, you'll get one entry back; if they stored several (up to three per call), you'll get them all.

**Request**

```json theme={null}
{
  "user_identifier": "string (required)",
  "verification_id": "string (UUID, required)",
  "gpg_public_key": "string (GPG public key block format, required)"
}
```

**Response**

```json theme={null}
{
  "backup": [
    {
      "backup_id": "string (UUID)",
      "backup_item_key": "string (e.g., 'seed_phrase')",
      "recovery_id": "string (UUID)",
      "recovery_package": {
        "algorithm": "gpg",
        "data": "string (base64, GPG-encrypted ciphertext)"
      },
      "original_filename": "string (if provided at store time)",
      "content_type": "string (if provided at store time)",
      "original_size": "string (bytes, if available)",
      "metadata": {}
    }
  ]
}
```

Each item in the array is a `BackupRecoverItemResponse`. The `recovery_package` is a unified envelope — for retail, the algorithm is always `gpg` and `data` is the base64-encoded GPG ciphertext. If a particular item failed to recover, the entry will include an `error` field instead of `recovery_package` and `recovery_id`.

We use GPG for the return trip because it gives your customer a clear, well-understood mental model: their device generates a key pair, sends us the public half, and we encrypt the recovered backup to it. The plaintext only exists on the customer's device.

### Step 3 — Rotate and re-back-up

After a recovery, the new recovery code should be backed up via `POST /v1/backup/store` so the protection stays current. The store call requires an approved `verification_id`, so plan to perform the re-back-up on the same recovery session you just used to retrieve the data.

## What's next

<CardGroup cols={2}>
  <Card title="API reference" href="/retail-recovery/api-reference">
    Every endpoint, request, and response.
  </Card>

  <Card title="Testing & sandbox" href="/retail-recovery/testing">
    Simulating verification outcomes without real customers.
  </Card>
</CardGroup>
