vlayer docs

Verifying proofs from vouch

Under Development: This API is currently in development. Documentation and endpoints may change. For production use, please contact our team.

When using the vouch platform to generate web proofs, you’ll receive webhook responses that include both the proof and the associated data. This guide shows you how to extract and verify these proofs using the POST /api/v2.0/verify endpoint.

Webhook Response Structure

When a user completes verification through vouch, a webhook is sent to your configured webhookUrl containing:

  • requestId: Unique identifier for the verification request
  • dataSourceId: Identifier for the data source used in this verification
  • outputs: Aggregated extracted data from all web proofs
  • webProofs: Array of web proof objects, each containing:
    • outputs: Data extracted from this specific proof
    • presentationJson: The presentation object (for verification)
    • decodedTranscript: Human-readable HTTP request/response data

To receive webhook responses, you need to configure a webhookUrl when initiating the verification flow. See the Vouch Getting Started guide or Vouch Integration Example for details on setting up webhooks.

Extracting and Verifying a Web Proof

Step 1: Extract the presentation from webhook response

The presentationJson field contains the cryptographic proof that can be verified independently:

// Example webhook payload received at your endpoint
const webhookPayload = {
  requestId: "00000000-0000-0000-0000-000000000000",
  dataSourceId: "00000000-0000-0000-0000-000000000000",
  outputs: {
    "Country": "en-US",
    "Full Name": "Jane Doe"
  },
  webProofs: [
    {
      outputs: {
        "Country": "en-US",
        "Full Name": "Jane Doe"
      },
      decodedTranscript: { /* ... */ },
      presentationJson: {
        data: "014000000000000000af5e...",
        meta: {
          notaryUrl: "https://notary.production.vlayer.xyz/v2.0.0",
          websocketProxyUrl: "ws://proxy.vlayer.xyz:80"
        },
        version: "2.0.0"
      }
    }
  ]
};

// Extract first proof - production apps should make sure to verify all delivered proofs
const presentation = webhookPayload.webProofs?.[0]?.presentationJson;
if (!presentation) throw new Error("No webProofs or presentationJson found");
# Example webhook payload received at your endpoint
WEBHOOK_PAYLOAD='{
  "requestId": "00000000-0000-0000-0000-000000000000",
  "dataSourceId": "00000000-0000-0000-0000-000000000000",
  "outputs": {
    "Country": "en-US",
    "Full Name": "Jane Doe"
  },
  "webProofs": [
    {
      "outputs": {
        "Country": "en-US",
        "Full Name": "Jane Doe"
      },
      "decodedTranscript": { "..." },
      "presentationJson": {
        "data": "014000000000000000af5e...",
        "meta": {
          "notaryUrl": "https://notary.production.vlayer.xyz/v2.0.0",
          "websocketProxyUrl": "ws://proxy.vlayer.xyz:80"
        },
        "version": "2.0.0"
      }
    }
  ]
}'

# Extract first proof - production apps should make sure to verify all delivered proofs
PRESENTATION=$(echo "$WEBHOOK_PAYLOAD" | jq '.webProofs[0].presentationJson')

Step 2: Verify the presentation

Send the extracted presentation to the verification endpoint:

const verifyResponse = await fetch('https://web-prover.production.vlayer.xyz/api/v2.0/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <your-api-key>'
  },
  body: JSON.stringify(presentation)
});

const verificationResult = await verifyResponse.json();
console.log(JSON.stringify(verificationResult, null, 2));
curl -s -X POST https://web-prover.production.vlayer.xyz/api/v2.0/verify \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-api-key>" \
  -d "$PRESENTATION" | jq '.'

Expected Result

You should receive a JSON response with "success": true and the decoded request/response data (see below). Any other result should be treated as an invalid proof and must halt further processing or workflows.

{
  "apiVersion": "v2.0",
  "success": true,
  "traceId": "1c076b202cde4c9b84df2a9b1695fff3",
  "data": {
    "serverDomain": "www.amazon.com",
    "notaryKeyFingerprint": "a7e62d7f17aa7a22c26bdb93b7ce9400e826ffb2c6f54e54d2ded015677499af",
    "tlsTimestamp": 1748535526,
    "request": {
      "method": "POST",
      "url": "/prime-ajax/customername",
      "version": "HTTP/1.1",
      "headers": [
        "accept: application/json",
        "content-type: application/json;charset=UTF-8",
        "host: XXXXXXXXXXXXXX",
        "connection: XXXXX",
        "content-length: 91"
      ],
      "body": "{\"location\":\"PrimePageEngagementCXCards/...\"}",
      "raw": "504f5354202f7072696d652d616a61782f..."
    },
    "response": {
      "status": 200,
      "version": "HTTP/1.1",
      "headers": [
        "content-type: application/json",
        "transfer-encoding: chunked",
        "content-encoding: gzip",
        "date: Thu, 21 May 2026 14:18:46 GMT"
      ],
      "body": "{\"payload\":{\"fullName\":\"Jane Doe\",\"locale\":\"en-US\"}}",
      "raw": "485454502f312e3120323030204f4b..."
    }
  }
}

Sensitive header values are redacted with X characters in the response for privacy. See redaction configuration for details.