Enclave Verify

Enclave Verify

API Documentation

REST API reference for integrating identity verification and credential presentation into your product.

Authentication

All API requests must include your API key in the request body as api_key. Keys are prefixed enc_live_ for production and enc_test_ for test mode. Never expose your API key client-side.

Cryptography

New credentials use post-quantum ML-DSA-65 (FIPS 204) over a SHAKE256 Merkle root. Selective disclosure returns Merkle proofs - not the full claim set. Fetch Enclave's public signing keys from the well-known endpoint below.

ML-DSA public keys

GET https://verify.enclave.talk/.well-known/mldsa-public-key.json

{
  "@context": "https://verify.enclave.talk/contexts/mldsa/v1",
  "issuer": "verify.enclave.talk",
  "keys": [
    {
      "id": "https://verify.enclave.talk/.well-known/mldsa-public-key.json#2026-06-15",
      "type": "ML-DSA-65",
      "algorithm": "ML-DSA-65",
      "publicKey": "base64url_public_key",
      "created": "2026-06-15T00:00:00.000Z",
      "status": "active"
    }
  ]
}

The ML-DSA signature in each credential covers the Merkle root bytes only. Validate by recomputing disclosed claim leaves, verifying Merkle proofs against the root, then checking the ML-DSA signature with the published public key.

Endpoints

Create verification request

Request

POST https://verify.enclave.talk/api/v1/verify/request

{
  "api_key": "enc_live_...",
  "user_identifier": "user@example.com",
  "claims_requested": ["age_18_plus", "identity_verified"],
  "redirect_url": "https://yoursite.com/verified",
  "webhook_url": "https://yoursite.com/webhooks/verify"
}

Initiates a verification request for a user. Returns a URL to redirect the user to.

ParameterTypeRequiredDescription
api_keystringYesYour Enclave Verify API key
user_identifierstringYesUser's email or Enclave user ID
claims_requestedarrayYesClaims to verify. See supported claims below
redirect_urlstringYesWhere to send the user after verification
webhook_urlstringNoEndpoint to receive verification result

Response

{
  "request_id": "a1b2c3d4-...",
  "verify_url": "https://verify.enclave.talk/consent?request=a1b2c3d4",
  "status": "pending_consent",
  "expires_at": "2026-06-06T01:00:00Z"
}

Redirect your user to verify_url. The request expires after 1 hour.

Get request status

Request

GET https://verify.enclave.talk/api/v1/verify/request/:request_id

x-api-key: enc_live_...

Poll the status of a pending verification request.

Response (ML-DSA-65)

{
  "request_id": "a1b2c3d4-...",
  "status": "consented",
  "claims": {
    "age_18_plus": true,
    "identity_verified": true
  },
  "certificate_id": "uuid",
  "presented_at": "2026-06-06T00:05:00Z",
  "algorithm": "ML-DSA-65",
  "merkle_proofs": [
    {
      "leafIndex": 0,
      "siblings": ["base64url_sibling_hash"]
    }
  ],
  "credential": { "...": "W3C VC 2.0 envelope" },
  "signature": "base64url_mldsa_sig_over_merkle_root"
}
StatusDescription
pending_consentAwaiting user action
consentedUser approved - claims returned
deniedUser denied the request
expiredRequest expired after 1 hour

Present certificate

Request

POST https://verify.enclave.talk/api/v1/verify/present

{
  "api_key": "enc_live_...",
  "user_identifier": "user@example.com",
  "claims_requested": ["age_18_plus"]
}

Request a credential presentation for a previously verified user. Billed at $0.025 per call.

Response (ML-DSA-65)

{
  "certificate_id": "uuid",
  "claims": {
    "age_18_plus": true
  },
  "presented_at": "2026-06-06T00:05:00Z",
  "algorithm": "ML-DSA-65",
  "merkle_proofs": [
    {
      "leafIndex": 1,
      "siblings": ["base64url_sibling_hash", "..."]
    }
  ],
  "credential": {
    "@context": ["https://www.w3.org/ns/credentials/v2"],
    "type": ["VerifiableCredential", "EnclaveVerifyCredential"],
    "credentialSubject": {
      "merkleRoot": "hex_shake256_root",
      "claimCount": 2
    },
    "proof": {
      "type": "MLDSASignature2024",
      "proofValue": "base64url_mldsa_signature"
    }
  },
  "signature": "base64url_mldsa_sig_over_merkle_root"
}

Only claims the user previously consented to share will be returned.

Validate signature

Request (ML-DSA-65)

POST https://verify.enclave.talk/api/v1/verify/validate

{
  "certificate_id": "uuid",
  "disclosed_claims": {
    "age_18_plus": true
  },
  "merkle_proofs": [
    {
      "claimKey": "age_18_plus",
      "claimValue": true,
      "proof": {
        "leafIndex": 1,
        "siblings": ["base64url_sibling_hash"]
      }
    }
  ]
}
ParameterTypeRequiredDescription
certificate_idstring (uuid)YesCertificate that was presented
disclosed_claimsobjectYes*Claim key/value pairs being verified
merkle_proofsarrayYes*Per-claim Merkle proof paths from /present

Validate a credential presentation by checking Merkle proofs and the ML-DSA signature over the stored root. No authentication required. Free - not billed.

Response

{
  "valid": true,
  "issuer": "verify.enclave.talk",
  "verified_at": "2026-06-06T00:05:00Z",
  "algorithm": "ML-DSA-65"
}

Supported claims

ClaimTypeDescription
identity_verifiedbooleanGovernment ID validated
age_18_plusbooleanUser is 18 or older
age_21_plusbooleanUser is 21 or older
name_verifiedbooleanName on document confirmed
nationalitystringISO country code
document_typestringpassport, drivers_license, national_id

Webhooks

Enclave signs every webhook with HMAC-SHA3-256. The algorithm is sent in x-enclave-signature-algorithm. Validate the signature before processing.

Verification consented (ML-DSA-65)

{
  "event": "verification.consented",
  "request_id": "uuid",
  "claims": {
    "age_18_plus": true,
    "identity_verified": true
  },
  "certificate_id": "uuid",
  "presented_at": "2026-06-06T00:05:00Z",
  "algorithm": "ML-DSA-65",
  "merkle_proofs": [{ "leafIndex": 0, "siblings": ["..."] }],
  "credential": { "...": "W3C VC 2.0 envelope" },
  "signature": "base64url_mldsa_sig_over_merkle_root"
}

Verification denied

{
  "event": "verification.denied",
  "request_id": "uuid",
  "denied_at": "2026-06-06T00:05:00Z"
}

Signature validation

import crypto from 'crypto'

const sig = req.headers['x-enclave-signature']
const algorithm = req.headers['x-enclave-signature-algorithm'] ?? 'sha3-256'

if (algorithm !== 'sha3-256') {
  return res.status(401).json({ error: 'Unsupported signature algorithm' })
}

const rawBody = JSON.stringify(req.body)
const expected = crypto
  .createHmac('sha3-256', process.env.ENCLAVE_WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex')

if (sig !== expected) {
  return res.status(401).json({ error: 'Invalid signature' })
}

Error codes

CodeHTTP StatusDescription
invalid_api_key401API key missing, invalid, or revoked
user_not_found404No Enclave account for this identifier
no_certificate404User has not completed verification
certificate_expired410Certificate expired - user must re-verify
claim_not_available400Requested claim not in user's certificate
request_expired410Verification request expired
insufficient_funds402Billing not set up - connect a payment method in the business dashboard