arrow-left

All pages
gitbookPowered by GitBook
1 of 9

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Overview

hashtag
Prerequisites

Before you get started with the zkPass API, here's what you'll need:

  1. API Credentials

  2. User Data Token

  3. DVR Token

hashtag
How to Access the API

The zkPass API uses a familiar approach called basic authentication to ensure secure access. Here's what you'll need:

  • API Key: This unique identifier acts like your username for the API.

  • API Secret Key: Consider this your password, but much more secure. Keep it confidential!

circle-info

Register on the to obtain your API and secret keys.

hashtag
Including Your Credentials:

To access the API, you'll need to provide both your API Key and API Secret Key in every request you make. They should be included in the authorization header of your requests.

hashtag
Don't Have Credentials Yet?

No worries! If you haven't received your API credentials, simply fill out our easy-to-use to request them. We'll get you set up in no time.

hashtag
Utilities

Our utilities section provides a detailed guide on creating a key pair. This key pair is used for signing and encrypting your user data token (and DVR token, if needed). Here's a quick overview of the steps involved:

zkPass portalarrow-up-right
formarrow-up-right
Generate Key Pair
Sign User Data and DVR
Encrypt User Data and DVR

Utilities

This guide complements your understanding of our API endpoints by delving into key generation, signing, and encryption. These steps are crucial for securing your data tokens when using zkPass.

hashtag
Key Pair Generation

This pair consists of two keys: a public key and a private key. Think of them like a lock and key combination.

  1. Public Key: This key is freely shared and used to verify the authenticity of your signed data tokens.

  2. Private Key: Keep this key safe! It's used for signing and decrypting your data tokens.

hashtag
Signing for Trust: Securing Your Data Tokens

Signing your data tokens adds a digital signature using your private key. This signature acts like a tamper-proof seal, ensuring anyone receiving the token can confirm it hasn't been altered.

hashtag
Encryption for Privacy: Keeping Your Data Confidential

Encryption takes your data tokens and scrambles them using your private key. This makes the data unreadable by anyone who doesn't possess the corresponding public key (which you wouldn't share).

Endpoints

This section provides a detailed reference for all zkPass API endpoints

RESTful API

This section explains how to use the zkPass service using any programming language that we don't currently support.

Generate Key Pair

hashtag
Overview

We are using elliptic curve cryptography (ECC) for our encryption. The key pair should be generated using P-256 curve. Ensure the generated key pair is in PEM format.

The public key should be encoded in SPKI format, and the private key should be encoded in PKCS#8 format.

hashtag
Public Key Format

  1. PEM format

  2. SPKI encoding

hashtag
Private Key Format

  1. PEM format

  2. PKCS#8 encoding

hashtag
Example Implementation

Here's the example of generating key pair in Typescript.

hashtag
Output Example

After this section you should have a key pair consisting of :

  1. publicKeyJWKS

  2. privateKey

circle-info

Ideally, the issuer & verifier should have different key pairs.

Make sure you have both them before proceeding to the next section.

Here's the example for publicKeyJWKS and privateKey.

Generate Proof

hashtag
Prerequisites

Before using the Generating Proof Endpoint, There are three essential items you'll need to use the endpoint:

  1. encryptedUserDataToken : This token contains your encrypted user data

encryptedDvrToken : This token holds your encrypted DVR data

  • apiToken : This unique identifier authenticates your requests with the zkPass API

  • circle-info

    apiToken is the base64 form of YOUR_API_KEY:YOUR_API_SECRET.

    hashtag
    Generating Encrypted Tokens:

    For instructions on creating the encryptedUserDataToken and encryptedDvrToken, refer to our Utilities section. It provides a step-by-step guide that covers:

    1. Generating a Key Pair: This creates the keys you'll use for signing your data.

    2. Signing User Data and DVR: This step adds a digital signature to your data tokens, ensuring their authenticity.

    3. Encrypting User Data and DVR: This encrypts your data tokens, making them unreadable by anyone without the decryption key.

    hashtag
    Endpoint

    Type
    Value

    HTTP Method

    POST

    API endpoint (Playground)

    https://playground-zkpass.ssi.id/api/1.0/dvr/1.0/proof

    hashtag
    Request Header

    Parameter
    Description

    Authorization

    base64 form of YOUR_API_KEY:YOUR_API_SECRET

    Content-Type

    Indicates that the content being sent or received is JSON data

    hashtag
    Request Body

    Parameter
    Type
    Mandatory
    Description

    user_data_token

    string

    Mandatory

    encrypted user data token in JWE format

    dvr_token

    string

    Mandatory

    encrypted dvr token in JWE format

    circle-info

    Please see example of encrypted User Data and DVR here.

    hashtag
    Response

    import crypto from "crypto";
    
    interface PublicKeyJWKS {
      x: string;
      y: string;
      kid: string;
    }
    
    function generateKeyPair() {
      const keypair = crypto.generateKeyPairSync("ec", {
        namedCurve: "prime256v1",
        publicKeyEncoding: { type: "spki", format: "pem" },
        privateKeyEncoding: { type: "pkcs8", format: "pem" },
      });
      const lines: string[] = keypair.publicKey.trim().split("\n");
    
      const x = lines[1];
      const y = lines[2];
    
      const kid = "kid-for-your-key-pair";
    
      const publicKeyJWKS: PublicKeyJWKS = {
        x,
        y,
        kid,
      };
    
      const privateKey: string = keypair.privateKey;
      console.log({ publicKeyJWKS, privateKey });
      ...
    }
    {
      publicKeyJWKS: {
        x: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELOmrNI4A9ML4iGJXpYlaZiYGVCxB',
        y: 'k+evjhOZEbCLj17o/ZdfEv7dUZIRKRoZ1bud5Gq8OCItDlXkTyMrtWrhdA==',
        kid: 'q6ZFSOJcTiZWJWkvUshpFw5v20xstZN/T4lt4zpKsUg='
      },
      privateKey: '-----BEGIN PRIVATE KEY-----\n' +
        'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrSuv5exgvZGmELOL\n' +
        'RkT9fhhRxKW3SQASrTVbENIN5cKhRANCAAQs6as0jgD0wviIYleliVpmJgZULEGT\n' +
        '56+OE5kRsIuPXuj9l18S/t1RkhEpGhnVu53karw4Ii0OVeRPIyu1auF0\n' +
        '-----END PRIVATE KEY-----\n'
    }
    Authorization: Basic [apiToken]
    Content-Type: application/json
    {
        "user_data_token": "[encryptedUserDataToken]",
        "dvr_token": "[encryptedDvrToken]"
    }
    {
        "status": 200,
        "proof": "eyJ0e..."
    }

    Encrypt User Data and DVR

    hashtag
    Prerequisites

    Make sure you have :

    1. userDataToken

    2. dvrToken

    Read section for detail info.

    hashtag
    Overview

    zkPass enhances the security of your data during transport by encrypting it before sending it over the network. The are only two entities that can access the data: the holder and zkPass host running in a Trusted Execution Environment (TEE).

    hashtag
    Example Implementation

    First, find the zkPass public key in the .well-known/jwks.json file at this endpoint: The key you're looking for has a kid (Key ID) of "ServiceEncryptionPubK".

    hashtag
    Output Example

    After this section you should have :

    1. Encrypted User Data Token (in JWE Format).

    2. Encrypted DVR Token (in JWE Format).

    Here's the example

    Sign User Data and DVRarrow-up-right
    https://playground-zkpass.ssi.id/.well-known/jwks.jsonarrow-up-right
    import { importSPKI, EncryptJWT } from "jose";
    
    async encryptData(userDataOrDvrToken: string): Promise<string> {
        const keyUrl = 'https://playground-zkpass.ssi.id/.well-known/jwks.json';
        const fetchKeys = await fetch(keyUrl);
        const keys = await fetchKeys.json();
        const encryptionPubKey = keys.find(
          (key: { kid: string }) => key.kid === 'ServiceEncryptionPubK'
        );
        const zkPassPublicKey =
          '-----BEGIN PUBLIC KEY-----\n' +
          encryptionPubKey.x +
          '\n' +
          encryptionPubKey.y +
          '\n-----END PUBLIC KEY-----';
        const importedPublicKey = await importSPKI(zkPassPublicKey, "ES256");
        return await new EncryptJWT({ data: userDataOrDvrToken })
          .setProtectedHeader({
            alg: "ECDH-ES",
            enc: "A256GCM",
          })
          .encrypt(importedPublicKey);
      }
    {
      "encryptedUserDatatoken": "eyJ0eXAiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoielJJdWp5aDdmbG84OU5MbHFweEt0VWpmckhabVd1NVIzNGNkTlhnLVMzUSIsInkiOiJwNHN6LXc3U3pGRTBMbGY2R0Z4cUd2YmtuSHpEaENHY2dMSXZvTHpIbTlZIn0sImFsZyI6IkVDREgtRVMifQ..STqfLlyIzO-vlPu-.ju8WoPSCiLBSIUNw9EzSVjINBVx5NzN_XXb4jd8GjQ8d9USaQ0i9yU2M-kXf-eD8PZDYDvQ3hvuIi25MlRlyf_vWQSIrFuReHmZxQGZE1pcg7QXYRip0u4cfUQotvGjTSsBsa_bdXvH7gAInmalzAW_KrI08yXpT-CylitTwhmlm1RdVGWvGsMlu7u6vWWFHpOgHskN50ExfAWVa5_w93euEbxOcGjgegQTGxJocfyglmXIvIlW4XhwG8sj3OotdJbHvoqFMOfjkDf27HlMgG1jvjB-Af4CpIbdp3oP0suc_aDoayYG7butqMqrgk-OPcyIC5ELyDMWErx5WzPc1DY3icRStyzG2yPbs49l2gCcnubrwVbFZu-BqIE5hB6W7A_3Vfd479Heaec_sKzFJUlj9issy4vydy2Lsp_ldaP3J5p1tYvhxDGvoCvolkGHzwfjaNlciJ1BEfwQxyeipxrkXqzQ8oSgY7q5IPe7PtZ7O7JTpwvwbT04opAQiHatnnPbdlsCECSHj1UHuBNEfXcyK7gdzo9OaYOTPcD_Gbd88Yd8DxbKVH3e3M3z7O-ipqhD9vcuw92iOF9e1q23P9MXqjWjylDxSx4xWf9-imEW8aeGDulIQQBJ8OJZv9I7_eUiBTMWxxGgmcHsLSt7Chcv6uk3qj7yoxRqf22XvnxixOw3EtaVchxglqcdsV4dIWN5ynaJb67qUv2vFyig8XvS3x2b8bHzqS0_rNQy8L6ukzK1U0ndSW3QSqi0hcQ_CEwGzKNi445IhJ-NU_a2eGI_PiLhI2M00zpVjIIkc_bGOWHaB-3yCijUS0NjDhET78KWMTmkAEDPS0JVXni3okEd4mXhskbRr6y4OH-ShANti-FE69TO5KZmKZHkuY41xPO8_XNr1sL61z-LOsWb1nVHwfClUBUvFatIy2ElkxpjAhR-FhEiwk_oq6TXwBwLHEpQ8uJ1Es8jMMz0dtZVAo6D6OoutOH5Bo8JDtXX5uJZ7sY-GQgQX1dKLozbShF5IG40uEoGE_dIrd8MHfYZ_rqFct5tobRw299rCUMS3fcLHsJj1fqZgOGlzpy6ruBUNKZPpmNGzZcUiu2zcVheUKhjr69I7LV8h5gkb4LHkx0_AK8Lxl-jgLo-C3xGDIfG0dnZ3zoLDrK0IaueQNmxX-66rlHWhSCV-pSgJpXUFd-zv-IZix_tfV3svEaHyP9tYDg7MHus9mYkyBS6IyJqtEdmsoEYHwohVCS_lL51oIragrVtNLqXDV784zyEKKNB1XwQt5jL-XdOmrsGlJgzp2gUeeEwonEX0-LniBipeuVIAsFgXJ0ThNsLzm61BSLi5wL4xcyzRh9Bws6FRjchxEPkIq8AeQZaTZOddBx1xT4-vcMtnlAAy5ON10bCULCWm-jaAdI6UGSFur106Nr14Jj1dAFIkPFq1EyLFn_2E5D1RDp3siVJ1GCmCdNeVpy3TiHnmhaCXwZ5QkO4yLru4a8s6yWtL9P4Vktcli6tFMJ68RwdJP98YzPbvtnVjlcSei7B2lorhinEGr0JRJDZVhxujvTaYLlE2bFr1UgrAH8Sr9jpViv4Y2Ux2mFKCPdJXaWPsMc1t_s1JCjavdUnskW6afK0OOquHqoMZZoBJgNPq1HXW04Crkl5dM4D6OunA6afvUqxjpzItUlveN-chXInzVv2hX3ZFl8rWyAG3U0fR9_QJJWMzKGmu.P8Ov7hrTkyYla1iTlmXptg",
      "encryptedDvrToken": "eyJ0eXAiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiSXQ0YXljSklYUUdFQWtTUjJfU3ZVRWU2QThHaGtscUkxVGxlNmR1MjFVWSIsInkiOiJNSGctcGU2RjhEUDJVbFhMZW1fd1F4NnFIVUdRS0prNzVqel96a3JnOGpjIn0sImFsZyI6IkVDREgtRVMifQ..CiVm0RozjNyk8DhZ.e2iJK7BzHqObLeB1mxO3kJo_iPZIh8H5p3nV3ZN_MtQasX2vg2jeKyGBFgklILwo9d9Sva83ZF0fOla6L9jdAUrQqXBehoWDwTLir3vjLaqITe3bsKmUIdDInmIaZHeoH3RkTn4PqPsBFDj7zFpTwCWeKBDKgZtckJ1N8gU5R-ohAnwZDzsbinGQ03x8w5UeIlmJyZcSL4MeV1NcSVSXryZIi-w_xuK3wr4JGBqSw993tArrxPfF9jENZfqrLczrYHZ08RW_CZBfjp1TxeQGM2ovlw672T-Fu_EA8giqaK4Oqi5u_a_Ixp6bHuGldtf7EeFcoSgoMP5qoK-nIkkW5mJyRqwwO0wTaYVoohfhYliTrlBdO92BW-lK9LqVjAYEG7H1LeKOmP8e2Ze42w_0BeOMlilLE6xyxSVmGZkqnTpehuIgYZmfbO-ps5pWEFXLQ5pba5tCr_jzl3-F9dGDaT3Gpvo8PM1Hn7fAKHgD6_kPaMm1e2MB8gzcfmjwBjg6M3TQ8P1alNFYgsloxCyMhiwMFx3yxKGfoDBAY-vQtaTyWT57INXFjE9DyLSm8pXxbQmA51bSt9Ke1OoTweSR1a_tBX3jMmNnrROPhS0rDQZfiCntytSuiLqIfiBv7jZOW0nH3BoUJ4DFB7j5P6inqdCl5qRRz1fz78Egij9aXuzXpD79bD6i7by8NTAJAVoXOuEPB8VFFjFA0rx3edxB3CWsGZD5lB64iGNo9WH5YdoxHW1ox6a1Vp0oHb1NYhVlxCMiLjs6R8FD7kFw-Dwl5WydUCyjO50_y_fBIAsUvTcfqS_7vCuvp-2d4WdXtBV8pdXyt-T8DCIMYObDp-X6syO7rl40L2i998euEj1cUJ4hjC9keNHRWQul6O1gIfLdZBamOpWGGQnjsHmd38lEqtxpPS0sjzPuRvjc9bg1zW0TOGslABZyt_Ura0xNr2aLFq8DTgP5F9cAKE-vSPuyhRAcj0BzgnYuxbcYblj2OPkxF5AkLmzNI4M5Bs5VVfrkN5fnlof0JuCfa7ocvCZgS01OMm-iOxeoZNlLbQz6zqPvkgfJDdsih7wwfCxkkL9m-XkGdE1_GYvaRN12EkG2gXfiYF1AxiZkdihDhU-EjHXUYnZar2TXdhjtO6zjAxDV6Iezp_O0krCXrFu95-dV_4xjexjD2vuy3QDRbsBYun9NI-nH55U_sXS8ztdPhTAG03XPMwMZmPbu1cXUZKTNIHmYA6Qr0zPwTm-BkZfZvi9dnKLEtH2LfZKGOvG6FamjarxS7-spLVrswVhbPsWugjcxfJruzaoi6XjlrMXQo5qB0yXfML5gEQEHJQHPu35C5etR8VLiUv9M7kwj-ld-7FW2M1jz6T6KktxieJseZl_YzOUdgTazSfk4zGyc8J68NQ1hIQ-uJ0GgMYvU54WOr6n_pOBKMfL_BYcNSCOFBR0YXnisFtf-r1YpfKQ3ILUmpsxS1nnTKAK8VOO1LpaItUjLrk56p-xYgdQBhnJ9jZ8xpsb7yoQqsTsug8LVn01yVyYihj2Bf6LmCsFpoDMTkN1Or491xaN0m-g4SpUPsyBPnmYvUhfUhPUw4eYXLAImWm6OxSN3cGSk3AKmt1kMRlW-ZLgAO26ecQSuYMUHhM4nJhV-aab62YEfXVB9B_nF7zPEgDS2uS7RlwIR2e-cgjzl_Lmm4pDGcvDyxp_Z7daYODMdJbwEdvE65U69K2Z_HMDLvl6Pc-jO-Y8-I5R4KMBlFuNf5i2b3RnTqRAVLwx2AJpLp55kysaGfpvH4s-9XPnRjZPjQVFIBZJtDAhJG790OsXSIxMh1nIHbyPPX7JKMVmiN_N3TeTZifEGin9IrSGPCi4z24Vn40T4MiYQEFs5vtLPoqiScm4fx9lnDUJY7F5IQn3OTU4K4XZFmm8eZkK7_gfNsylQ8aCn4GmK8X70NW4KR24eU29mt35-SCaxwYZYPopUFWo_LzyTrZug3AIqAc6AeUsU8mrK5A0rFGFboDGYHnnc-Q0Ffrnp4C64YS2E2jfe_-KfIjAwfEBigfghHhoBpX_RC5zLVoFrblJTjscgAgE6ZcVigmZPEAGMf4iQ5E8zTtrVYLlim12TEvRSNtro5wpZXgG0XuD-Qgja0V80dwfdL0QX3S_qre1d8Za43SqoZc5CEGTP98Po8GPIIpwD_006uB6qcvVVQW5BNw7KVRD48jcph3kXpGhzbkmDA5pDTfjWbbgYuFQetMdrrvGNrjr3.iLsHlasWc7GptHe5pdwD_Q"
    }

    Errors

    The zkPass RESTful API strives to provide a smooth user experience, but occasionally errors may occur. This section equips you with knowledge about potential error responses you might encounter.

    hashtag
    Error Response

    {
        "status": [http_status_code],
        "status_code": [status_code],
        "status_text": [error_message]
    }

    HTTP Status Code
    Status Code
    Error Message
    Description

    Something went wrong. Contact the administrator if the error persists

    Our system failed to create a new instance of the privacy app

    500

    CUSTOM_ERROR

    [Custom message]

    A specific error occurred that requires custom handling (message varies)

    500

    MISSING_APPS_CONFIG

    Something went wrong. Contact the administrator if the error persists

    The required configuration for privacy apps is not found or inaccessible

    500

    ERROR_LOCKING_SOCKET

    Something went wrong. Contact the administrator if the error persists

    Our system failed to establish exclusive access to the communication socket

    500

    ERROR_SENDING_TO_SOCKET

    Something went wrong. Contact the administrator if the error persistsrver is not ready yet, please try again later

    Our system failed to send data through the communication socket

    500

    ERROR_RECEIVING_FROM_SOCKET

    Something went wrong. Contact the administrator if the error persists

    Our system failed to receive data from the communication socket

    500

    ERROR_DESERIALIZING

    Something went wrong. Contact the administrator if the error persists

    Our system failed to parse the output data from the privacy app

    400

    INVALID_PARAMS

    Invalid parameter(s).

    The provided parameters don't meet the required format or validation rules for the app input

    400

    INVALID_PARAMS

    Error serializing parameters

    Our system failed to convert the provided parameters into the required format

    404

    INVALID_URL

    Invalid URL

    The requested privacy app endpoint or resource was not found in our system

    500

    ERROR_LOADING_APP

    Something went wrong. Contact the administrator if the error persists

    Our system failed to initialize or load the requested privacy app

    500

    ERROR_LOADING_INSTANCE

    Sign User Data and DVR

    hashtag
    Prerequisites

    Make sure you have key pair consisting of :

    1. publicKeyJWKS

    2. privateKey

    Read section for detail info.

    hashtag
    Overview

    We need to sign and before sending a request to the zkPass service. This ensures that the payload is not tampered during transport.

    hashtag
    Example Implementation

    hashtag
    JWKS

    circle-info

    The JSON Web Key Set (JWKS) is a set of keys containing the public keys used to verify any JSON Web Token (JWT) issued by the Authorization Server and signed, You can find a complete definition of JWKS .

    Upload your publicKeyJWKS so that it's accessible from the internet. This will be used by zkPass service to verify the validity of the user data.

    Example of the uploaded publicKeyJWKS

    hashtag
    JWS

    circle-info

    A JSON Web Signature (abbreviated JWS) is an IETF-proposed standard (RFC 7515) for signing arbitrary data. This is used as the basis for a variety of web-based technologies including JSON Web Token. You can find a complete definition of JWS .

    This is an example code of how you can sign a JSON object as JWS format in Typescript. Let's say you uploaded your publicKeyJWKS to https://mywebsite/my-keys.json

    Example for User Data

    Below is the example of Query in DVR, this query will be included in full DVR

    Below is the example of full DVR that will be generated into DVR Token

    hashtag
    Output Example

    After this section you should have :

    1. User Data Token : User Data in JSON Web Signature (JWS) format.

    2. DVR Token : DVR in JSON Web Signature (JWS) format.

    Here's the example of User Data Token & DVR Token in JWS format.

    Generate Key Pairarrow-up-right
    User Dataarrow-up-right
    Data Verification Request (DVR)arrow-up-right
    herearrow-up-right
    herearrow-up-right
    {
      "keys": [
        {
          "x": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELOmrNI4A9ML4iGJXpYlaZiYGVCxB",
          "y": "k+evjhOZEbCLj17o/ZdfEv7dUZIRKRoZ1bud5Gq8OCItDlXkTyMrtWrhdA==",
          "kid": "q6ZFSOJcTiZWJWkvUshpFw5v20xstZN/T4lt4zpKsUg="
        }
      ]
    }
    import { SignJWT, importPKCS8 } from "jose";
    
    async signDataToJwsToken(
        privateKey: string,
        userDataOrDVR: any
      ): Promise<string> {
        const verifyingKeyJwks = {
          jku: "https://mywebsite/my-keys.json",
          kid: "q6ZFSOJcTiZWJWkvUshpFw5v20xstZN/T4lt4zpKsUg="
        }
        
        const alg = "ES256";
        const importedPrivateKey = await importPKCS8(privateKey, alg);
        
        return await new SignJWT({ data: userDataOrDVR })
          .setProtectedHeader({
            alg: alg,
            jku: verifyingKeyJwks.jku,
            kid: verifyingKeyJwks.kid,
          })
          .sign(importedPrivateKey);
      }
    {
      "testID": "SCREEN-7083-12345",
      "testName": "QualityHealth Comprehensive Screen",
      "testDate": "2023-08-27T14:00:00Z",
      "lab": {
        "name": "QualityHealth Labs",
        "ID": "QH801874",
        "address": "1234 Elm St, Oakland, USA"
      },
      "subject": {
        "firstName": "Jane",
        "lastName": "Doe",
        "dateOfBirth": "1985-12-12",
        "bloodType": "A+",
        "DNAInfo": {
          "markers": {
            "APOE": ["E3", "E3"],
            "BRCA1": "Normal",
            "MTHFR": ["C677T", "A1298C"]
          },
          "haplogroups": {
            "paternal": "R1b1",
            "maternal": "H1a1"
          }
        },
        "contact": {
          "email": "jane.doe@gmail.com",
          "phone": "650-555-1234"
        },
        "address": {
          "street": "789 Oak Street",
          "city": "San Jose",
          "state": "CA",
          "zip": "95134"
        }
      },
      "measuredPanelsNgML": {
        "amphetamines": 0,
        "cocaine": 8,
        "opiates": 102,
        "benzodiazepines": 0
      }
    }
    [
      {
        "assign": {
          "blood_test_status": {
            "and": [
              {
                "==": [{ "dvar": "lab.ID" }, "QH801874"]
              },
              {
                "==": [{ "dvar": "testID" }, "SCREEN-7083-12345"]
              },
              {
                "~==": [{ "dvar": "subject.firstName" }, "jane"]
              },
              {
                "~==": [{ "dvar": "subject.lastName" }, "doe"]
              },
              {
                "==": [{ "dvar": "subject.dateOfBirth" }, "1985-12-12"]
              },
              {
                "==": [{ "dvar": "measuredPanelsNgML.amphetamines" }, 0]
              },
              {
                "<=": [{ "dvar": "measuredPanelsNgML.cocaine" }, 10]
              }
            ]
          }
        }
      },
      { "output": { "result": { "lvar": "blood_test_status" } } }
    ]
    {
      "zkvm": "r0",
      "dvr_title": "My DVR",
      "dvr_id": "d5bba9dc-e90c-456d-811c-79aa73755f54",
      "query_engine_ver": "1.3.0",
      "query_method_ver": "24555fe163e523bb313df8355ca39fbd79d49a02c642373b19c2a31cfc7a78d",
      "query": "[{\"assign\":{\"blood_test_status\":{\"and\":[{\"==\":[{\"dvar\":\"lab.ID\"},\"QH801874\"]},{\"==\":[{\"dvar\":\"testID\"},\"SCREEN-7083-12345\"]},{\"~\":[{\"dvar\":\"subject.firstName\"},\"jane\"]},{\"~\":[{\"dvar\":\"subject.lastName\"},\"doe\"]},{\"==\":[{\"dvar\":\"subject.dateOfBirth\"},\"1985-12-12\"]},{\"==\":[{\"dvar\":\"subject.contact.email\"},\"jane.doe@gmail.com\"]},{\"==\":[{\"dvar\":\"measuredPanelsNgML.amphetamines\"},0]},{\"<=\":[{\"dvar\":\"measuredPanelsNgML.cocaine\"},10]}]}}},{\"output\":{\"name\":{\"dvar\":\"subject.firstName\"}}},{\"output\":{\"email\":{\"dvar\":\"subject.contact.email\"}}},{\"output\":{\"result\":{\"lvar\":\"blood_test_status\"}}}]",
      "user_data_requests": [
        {
          "key": "",
          "value": {
            "jku": "https://raw.githubusercontent.com/gl-zkPass/zkpass-sdk/main/docs/zkpass/sample-jwks/issuer-key.json",
            "kid": "k-1"
          }
        }
      ],
      "dvr_verifying_key": {
        "jku": "https://raw.githubusercontent.com/gl-zkPass/zkpass-sdk/main/docs/zkpass/sample-jwks/verifier-key.json",
        "kid": "k-1"
      }
    }
    {
      "userDataToken": "eyJ0eXAiOiJKV1QiLCJqa3UiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZ2wtemtQYXNzL3prcGFzcy1zZGsvbWFpbi9kb2NzL3prcGFzcy9zYW1wbGUtandrcy9pc3N1ZXIta2V5Lmpzb24iLCJraWQiOiJrLTEiLCJhbGciOiJFUzI1NiJ9.eyJkYXRhIjp7InRlc3RJRCI6IlNDUkVFTi03MDgzLTEyMzQ1IiwidGVzdE5hbWUiOiJRdWFsaXR5SGVhbHRoIENvbXByZWhlbnNpdmUgU2NyZWVuIiwidGVzdERhdGUiOiIyMDIzLTA4LTI3VDE0OjAwOjAwWiIsImxhYiI6eyJuYW1lIjoiUXVhbGl0eUhlYWx0aCBMYWJzIiwiSUQiOiJRSDgwMTg3NCIsImFkZHJlc3MiOiIxMjM0IEVsbSBTdCwgT2FrbGFuZCwgVVNBIn0sInN1YmplY3QiOnsiZmlyc3ROYW1lIjoiSmFuZSIsIl9maXJzdE5hbWVfemtwYXNzX3B1YmxpY18iOnRydWUsImxhc3ROYW1lIjoiRG9lIiwiZGF0ZU9mQmlydGgiOiIxOTg1LTEyLTEyIiwiYmxvb2RUeXBlIjoiQSsiLCJETkFJbmZvIjp7Im1hcmtlcnMiOnsiQVBPRSI6WyJFMyIsIkUzIl0sIkJSQ0ExIjoiTm9ybWFsIiwiTVRIRlIiOlsiQzY3N1QiLCJBMTI5OEMiXX0sImhhcGxvZ3JvdXBzIjp7InBhdGVybmFsIjoiUjFiMSIsIm1hdGVybmFsIjoiSDFhMSJ9fSwiY29udGFjdCI6eyJlbWFpbCI6ImphbmUuZG9lQGdtYWlsLmNvbSIsIl9lbWFpbF96a3Bhc3NfcHVibGljXyI6dHJ1ZSwicGhvbmUiOiI2NTAtNTU1LTEyMzQifSwiYWRkcmVzcyI6eyJzdHJlZXQiOiI3ODkgT2FrIFN0cmVldCIsImNpdHkiOiJTYW4gSm9zZSIsInN0YXRlIjoiQ0EiLCJ6aXAiOiI5NTEzNCJ9fSwibWVhc3VyZWRQYW5lbHNOZ01MIjp7ImFtcGhldGFtaW5lcyI6MCwiY29jYWluZSI6OCwib3BpYXRlcyI6MTAyLCJiZW56b2RpYXplcGluZXMiOjB9fX0.TwoIFlHXnKjhKGlhW6tCfuc20pgvFFUmZ_wEMBvrQyKaub7aN3SBT0_zPPeDn_EUi2UP6eo0pr7ofNjfXEtgsA",
      "dvrToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJkYXRhIjp7Inprdm0iOiJyMCIsImR2cl90aXRsZSI6Ik15IERWUiIsImR2cl9pZCI6ImQ1YmJhOWRjLWU5MGMtNDU2ZC04MTFjLTc5YWE3Mzc1NWY1NCIsInF1ZXJ5IjoiW3tcImFzc2lnblwiOntcImJsb29kX3Rlc3Rfc3RhdHVzXCI6e1wiYW5kXCI6W3tcIj09XCI6W3tcImR2YXJcIjpcImxhYi5JRFwifSxcIlFIODAxODc0XCJdfSx7XCI9PVwiOlt7XCJkdmFyXCI6XCJ0ZXN0SURcIn0sXCJTQ1JFRU4tNzA4My0xMjM0NVwiXX0se1wifj09XCI6W3tcImR2YXJcIjpcInN1YmplY3QuZmlyc3ROYW1lXCJ9LFwiamFuZVwiXX0se1wifj09XCI6W3tcImR2YXJcIjpcInN1YmplY3QubGFzdE5hbWVcIn0sXCJkb2VcIl19LHtcIj09XCI6W3tcImR2YXJcIjpcInN1YmplY3QuZGF0ZU9mQmlydGhcIn0sXCIxOTg1LTEyLTEyXCJdfSx7XCI9PVwiOlt7XCJkdmFyXCI6XCJzdWJqZWN0LmNvbnRhY3QuZW1haWxcIn0sXCJqYW5lLmRvZUBnbWFpbC5jb21cIl19LHtcIj09XCI6W3tcImR2YXJcIjpcIm1lYXN1cmVkUGFuZWxzTmdNTC5hbXBoZXRhbWluZXNcIn0sMF19LHtcIjw9XCI6W3tcImR2YXJcIjpcIm1lYXN1cmVkUGFuZWxzTmdNTC5jb2NhaW5lXCJ9LDEwXX1dfX19LHtcIm91dHB1dFwiOntcIm5hbWVcIjp7XCJkdmFyXCI6XCJzdWJqZWN0LmZpcnN0TmFtZVwifX19LHtcIm91dHB1dFwiOntcImVtYWlsXCI6e1wiZHZhclwiOlwic3ViamVjdC5jb250YWN0LmVtYWlsXCJ9fX0se1wib3V0cHV0XCI6e1wicmVzdWx0XCI6e1wibHZhclwiOlwiYmxvb2RfdGVzdF9zdGF0dXNcIn19fV0iLCJxdWVyeV9lbmdpbmVfdmVyIjoiMS4zLjAiLCJxdWVyeV9tZXRob2RfdmVyIjoiMjQ1NTVmZTE2M2U1MjNiYjMxM2RmODM1NWNhMzlmYmQ3OWQ0OWEwMmM2NDIzNzNiMTljMmEzMWNmYzdhNzhkIiwidXNlcl9kYXRhX3JlcXVlc3RzIjp7IiI6eyJ1c2VyX2RhdGFfdXJsIjpudWxsLCJ1c2VyX2RhdGFfdmVyaWZ5aW5nX2tleSI6eyJLZXlzZXRFbmRwb2ludCI6eyJqa3UiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZ2wtemtQYXNzL3prcGFzcy1zZGsvbWFpbi9kb2NzL3prcGFzcy9zYW1wbGUtandrcy9pc3N1ZXIta2V5Lmpzb24iLCJraWQiOiJrLTEifX19fSwiZHZyX3ZlcmlmeWluZ19rZXkiOnsiS2V5c2V0RW5kcG9pbnQiOnsiamt1IjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dsLXprUGFzcy96a3Bhc3Mtc2RrL21haW4vZG9jcy96a3Bhc3Mvc2FtcGxlLWp3a3MvdmVyaWZpZXIta2V5Lmpzb24iLCJraWQiOiJrLTEifX19fQ.3sGHmQZ_lbEJei9OKTxlshDoYs2wWurjEyiA767BEBrxU5u5fSV-s1qHghVlp748MqZf_cLb7j6g3MrsL34wbw"
    }