> For the complete documentation index, see [llms.txt](https://bound-1.gitbook.io/bound-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bound-1.gitbook.io/bound-docs/developer-guide/api-reference/keystore.md).

# Keystore

## GET /api/keystore

> List passkey keystores — self for users, all for admin/super\_admin

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/keystore":{"get":{"operationId":"KeystoreController_paginate","summary":"List passkey keystores — self for users, all for admin/super_admin","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort field and order. Use - prefix for descending. Example: -createdAt, createdAt","schema":{"type":"string"}},{"name":"select","required":false,"in":"query","description":"Fields to select (comma separated). Use + prefix to include hidden fields. Example: name,status,+holders","schema":{"type":"string"}},{"name":"populate","required":false,"in":"query","description":"Relations to populate (comma separated). Example: wallet,token","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["keystore"]}}}}
```

## GET /api/keystore/passkey/{email}/credentialIds

>

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/{email}/credentialIds":{"get":{"operationId":"KeystoreController_getCredentialIds","parameters":[{"name":"email","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["keystore"]}}}}
```

## POST /api/keystore/passkey/request-link

> Device B initiates ECDH passkey sync — returns OTP session

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/request-link":{"post":{"operationId":"KeystoreController_requestLink","summary":"Device B initiates ECDH passkey sync — returns OTP session","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestLinkDto"}}}},"responses":{"201":{"description":""}},"tags":["keystore"]}}},"components":{"schemas":{"RequestLinkDto":{"type":"object","properties":{"email":{"type":"string","description":"Email of the account Device B wants to link to"},"pubKeyB":{"type":"string","description":"Device B ephemeral ECDH public key (base64 or hex)"}},"required":["email","pubKeyB"]}}}}
```

## GET /api/keystore/passkey/pending-link

> Device A polls for Device B public key to complete ECDH

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/pending-link":{"get":{"operationId":"KeystoreController_getPendingLink","summary":"Device A polls for Device B public key to complete ECDH","parameters":[{"name":"otp","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["keystore"]}}}}
```

## POST /api/keystore/passkey/approve-link

> Device A stores ECDH-encrypted keystore blob for Device B

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/approve-link":{"post":{"operationId":"KeystoreController_approveLink","summary":"Device A stores ECDH-encrypted keystore blob for Device B","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveLinkDto"}}}},"responses":{"201":{"description":""}},"tags":["keystore"]}}},"components":{"schemas":{"ApproveLinkDto":{"type":"object","properties":{"otp":{"type":"string","description":"OTP from the request-link session"},"encryptedForB":{"type":"string","description":"AES-GCM encrypted mnemonic, key derived via ECDH (base64)"},"pubKeyA":{"type":"string","description":"Device A ephemeral ECDH public key (base64 or hex)"}},"required":["otp","encryptedForB","pubKeyA"]}}}}
```

## GET /api/keystore/passkey/relay

> Device B polls for the ECDH-encrypted keystore blob from Device A

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/relay":{"get":{"operationId":"KeystoreController_getRelay","summary":"Device B polls for the ECDH-encrypted keystore blob from Device A","parameters":[{"name":"otp","required":true,"in":"query","description":"OTP from the request-link session","schema":{"type":"string"}},{"name":"email","required":true,"in":"query","description":"Email address — used to resolve the account for the relay key","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["keystore"]}}}}
```

## POST /api/keystore/passkey/confirm-link

> Device B registers its passkey and saves the decrypted keystore blob

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/confirm-link":{"post":{"operationId":"KeystoreController_confirmLink","summary":"Device B registers its passkey and saves the decrypted keystore blob","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmLinkDto"}}}},"responses":{"201":{"description":""}},"tags":["keystore"]}}},"components":{"schemas":{"ConfirmLinkDto":{"type":"object","properties":{"otp":{"type":"string","description":"OTP from the request-link session"},"email":{"type":"string","description":"Email address — must match the account that owns the OTP session"},"challengeId":{"type":"string","description":"Challenge ID from GET /auth/webauthn/challenge"},"credentialId":{"type":"string","description":"WebAuthn credential ID"},"webauthnPublicKey":{"type":"string","description":"DER/SPKI public key (base64)"},"attestation":{"type":"string","description":"CBOR attestation object (base64)"},"clientDataJson":{"type":"string","description":"WebAuthn clientDataJSON (base64)"},"authenticatorData":{"type":"string","description":"WebAuthn authenticatorData (base64)"},"encryptedBlob":{"type":"string","description":"Keystore blob encrypted with Device B derived key"},"deviceName":{"type":"string","description":"Human-readable device label (e.g. \"Work Laptop\")"}},"required":["otp","email","challengeId","credentialId","webauthnPublicKey","attestation","clientDataJson","authenticatorData","encryptedBlob"]}}}}
```

## DELETE /api/keystore/passkey/{id}

> Remove a passkey by ID (blocked if it is the last one)

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/passkey/{id}":{"delete":{"operationId":"KeystoreController_removePasskey","summary":"Remove a passkey by ID (blocked if it is the last one)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["keystore"]}}}}
```

## POST /api/keystore/srp/password-hint

> Send password hint to account email

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.bound.exchange","description":"Production"}],"paths":{"/api/keystore/srp/password-hint":{"post":{"operationId":"KeystoreController_sendPasswordHint","summary":"Send password hint to account email","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestPasswordHintDto"}}}},"responses":{"201":{"description":""}},"tags":["keystore"]}}},"components":{"schemas":{"RequestPasswordHintDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address of the account to send the password hint to"}},"required":["email"]}}}}
```
