Request data privately with zkConnect
This tutorial will walk you through how we built zkSub, an application enabling whitelisted wallets and GitHub accounts to register to a service with their email without revealing their eligible account thanks to zkConnect integration.
You can access the open-source repository of the demo:
These 2 repositories use the
@sismo-core/zk-connect-react
package to request the proof, to see a full example of the
@sismo-core/zk-connect-client
package, you can see
this branch
of the React repository.
We will use The Ethereum Community Conference (EthCC), the largest annual European Ethereum event focused on technology and community, as an example in this tutorial.
Indeed, EthCC wants to grant premium access to contributors to The Merge. As a number of Ethereum contributors are anonymous, they may want to avoid revealing their personal information to EthCC organizers while still proving that they contributed to The Merge and deserve a premium access.
For anonymous contributors to The Merge to attend EthCC, we will build zkSub that enables them to register an email address and subsequently prove they are contributors to The Merge in a privacy-preserving manner. These anonymous contributors will then receive premium access to EthCC via email.
To implement zkConnect, you'll need both a frontend and a backend. The frontend will request the proof, and the backend will verify it.
For this tutorial, we recommend using the Next.js stack, which is a full-stack React framework. Next.js also offers a deployment service called Vercel, which makes it easy to deploy your app in just two clicks.
To create a new Next.js project, run the following command:
yarn create next-app --typescript
That's it! Your frontend will be located in
src/pages/index.tsx
, and your backend will be located in src/pages/api
.Before you begin integrating zkConnect, you must create first a zkConnect app in the Sismo Factory. This step is mandatory to obtain an application Id (
appId
), which is required during the zkConnect development process.The
appId
will be used to compute the vault identifier which is the the unique identifier for a user creating a proof for your app. The vault identifier is simply the hash of the user vault secret and the appId.
If we had removed the appId from this simple calculus, we would have had the same vaultIdentifier for the same vaultSecret, effectively leaking information about a user that uses zkConnect on two different apps. The vaultId would just be the same between the apps and the user can be tracked if vault identifiers become public.
But if we introduce an appId, the vaultId is now different between apps and the same user will have two different vault ids between two different apps, effectively preserving the user privacy this time. 🤘

Register your zkConnect app in the Factory
You can register a zkConnect app here: https://factory.sismo.io/apps-explorer.
To create a zkConnect App, you need to login with Sign-In With Ethereum and click on “create a new zkConnect App”. You will need to register an App Name, a description and upload a logo alongside registering authorized domains. You will need to pay attention about authorized domains as far as these are all the urls from which the appId that will be created can be used for zkConnect. Here we only reference ethcc.io for example.
Once created, you should have all information about your app displayed in your profile:

Your zkConnect apps profile
The appId displayed on your app’s profile in the Factory is its unique identifier. You will use it to request proof from your users in your app’s frontend and to verify it in the backend.
For this guide, you can use your own
appId
, such as 0x8f347ca31790557391cec39b06f02dc2
.The core concept of ZK Connect is to enable users to selectively disclose statements to applications regarding pieces of personal data, that we call Data Shards, stored in their Data Vault. This is done through the use of zero-knowledge proofs. Data Shards originate from groups that can also be created in the Factory.
All groups created on the Sismo protocol are public, so if someone has already created a group that meets your needs, you can reuse it. You can explore all existing groups on our Factory explorer. If you have very specific needs, you can create your own group using our no-code interface or by following this tutorial to submit a pull request to our repository.
For the EthCC app, we will use the group with all users who contributed to The Merge named
the-merge-contributor
. When member of this group will add their account in their Data Vault, they will get the associated Data Shard and will be able to start proving statements from it (e.g. I own one shard of “contributors to The Merge” group) when using zkConnect.Now that we've decided to use this group in our app, we need to find its unique group ID (groupId) to use it in the zkConnect flow.

the-merge-contributor search result in the group explorer
It’s better to try it out yourself, but you should ultimately find the group’s unique id:
0x42c768bb8ae79e4c5c05d3b51a4ec74a
Congratulations! You now have an appId (
0x8f347ca31790557391cec39b06f02dc2
) and a groupId (0x42c768bb8ae79e4c5c05d3b51a4ec74a
), which means you can integrate zkConnect into your app. Let’s now focus on how to generate and verify the proof.Now that you have an app and the group that users should prove membership in, you need to send your users to the Sismo Vault app to generate the required proof. When redirected, users will be able to privately create a proof from their Data Vault and the personal piece of data that comes from the requested group in zkConnect before sending it to you.
To do that you will need to use one of our packages:
React / Next.js
Javascript / Typescript
First, you will need to import the following:
yarn add @sismo-core/zk-connect-react
After importing, you will be able to use the zkConnect button in your app.

zkConnect button
To do so, you have to use the
ZkConnectButton
component:import { ZkConnectButton, ZkConnectResponse } from "@sismo-core/zk-connect-react";
<ZkConnectButton
appId={"0x8f347ca31790557391cec39b06f02dc2"} // appId you registered
// Declare your dataRequest for the-merge-contributor group
dataRequest={{
groupId: "0x42c768bb8ae79e4c5c05d3b51a4ec74a"
}}
// get a response
onResponse={async (zkConnectResponse: ZkConnectResponse) => {
//Send the response to your server to verify it
//thanks to the @sismo-core/zk-connect-server package
//Will see how to do this in next part of this tutorial
}}
/>
With the
dataRequest
props, you can request your users to generate a proof for a requested groupId, here the group the-merge-contributor
.
The
dataRequest
has more optional parameters available:groupTimestamp
: This parameter specifies the timestamp of the group snapshot a user wants to prove membership in. By default, this timestamp is set to ‘latest’ to use the latest generated snapshot.requestedValue
: In groups, every account is associated with a value, which can represent the number of tokens staked, the voting power of the account, etc. TherequestedValue
parameter allows you to specify the value your user needs to have in the group to generate the proof. By default, it is set to 1.comparator
: The comparator can force users to prove that they have a value in the group greater than or equal (”GTE”
) to therequestedValue
or strictly equal (”EQ”
)to therequestedValue
. By default, it is“GTE”.
By clicking on this button your user will be redirected to the Data Vault App, to generate a proof for the specified group.
If you are not part of the group, feel free to use the optional
devMode
in your zkConnectConfig. Set the boolean field enabled as true and input a list of addresses that will be used instead of the actual group. Beware that this devMode should only be used when prototyping, if you keep such config in production, these dev addresses will be able to make proofs in their Vaults.
import { ZkConnectButton, ZkConnectClientConfig, ZkConnectResponse } from "@sismo-core/zk-connect-react";
const config: ZkConnectClientConfig = {
appId: "0x8f347ca31790557391cec39b06f02dc2", // ethcc appId I created
devMode: {
enabled: true, // will use the Dev Sismo Data Vault https://dev.vault-beta.sismo.io/
devAddresses : [ // Will insert these addresses in data groups as eligible addresse
"0x123...abc",
"0x456...efa"
]
}
}
<ZkConnectButton
config={config}
// Declare your dataRequest for the-merge-contributor group
dataRequest={{
groupId: "0x42c768bb8ae79e4c5c05d3b51a4ec74a"
}}
// get a response
onResponse={async (zkConnectResponse: ZkConnectResponse) => {
//Send the response to your server to verify it
//thanks to the @sismo-core/zk-connect-server package
//Will see how to do this in next part of this tutorial
}}
/>
After your user generates the proof, he will be automatically redirected back to your app.
The
onResponse
props will allow you to get the response containing the proof.
You can find the full frontend code snippet used for zkSub:
First, you will need to import the following:First, you will need to import the following:
yarn add @sismo-core/zk-connect-client
After importing, the first step is to create a
zkConnectConfig
for your client and initialize a new ZKConnect client instance with it:import { ZkConnect, ZkConnectClientConfig, DataRequest } from "@sismo-core/zk-connect-client";
const zkConnectConfig: ZkConnectClientConfig = {
appId: "0x8f347ca31790557391cec39b06f02dc2", // ethcc appId I created
}
const zkConnect = new ZkConnect(zkConnectConfig);
If you are not part of the group, feel free to use the optional
devMode
in your zkConnectConfig. Set the boolean field enabled as true and input a list of addresses that will be used instead of the actual group. Beware that this devMode should only be used when prototyping, if you keep such config in production, these dev addresses will be able to make proofs in their Vaults.
import { ZkConnect, ZkConnectClientConfig, DataRequest } from "@sismo-core/zk-connect-client";
const zkConnectConfig: ZkConnectClientConfig = {
appId: "0x8f347ca31790557391cec39b06f02dc2", // ethcc appId I created
devMode: {
enabled: true, // will use the Dev Sismo Data Vault https://dev.vault-beta.sismo.io/
devAddresses : [ // Will insert these addresses in data groups as eligible addresse
"0x123...abc",
"0x456...efa"
]
}
}
const zkConnect = new ZkConnect(zkConnectConfig);
With this ZkConnect instance, you can request your users to generate a proof for a requested groupId, here the group
the-merge-contributor
.// Declare your dataRequest for the-merge-contributor group
const THE_MERGE_CONTRIBUTOR_REQUEST = DataRequest({
groupId: "0x42c768bb8ae79e4c5c05d3b51a4ec74a"
});
// The `request` function sends your user to the Sismo vault app to generate the proof.
zkConnect.request({
// we request a proof for a specific group
dataRequest: THE_MERGE_CONTRIBUTOR_REQUEST,
});
// get a response
const zkConnectResponse = zkConnect.getResponse();
By calling the
request
function, your users will be redirected to the Sismo Vault app to generate a proof for the specified group. The getResponse
function will allow the user to send the proof to your frontend by being redirected when the proof generation is done in their Data Vault.The
dataRequest
has more optional parameters available:groupTimestamp
: This parameter specifies the timestamp of the group snapshot a user wants to prove membership in. By default, this timestamp is set to ‘latest’ to use the latest generated snapshot.requestedValue
: In groups, every account is associated with a value, which can represent the number of tokens staked, the voting power of the account, etc. TherequestedValue
parameter allows you to specify the value your user needs to have in the group to generate the proof. By default, it is set to 1.comparator
: The comparator can force users to prove that they have a value in the group greater than or equal (”GTE”
) to therequestedValue
or strictly equal (”EQ”
)to therequestedValue
. By default, it is“GTE”.
After your user is redirected to your app, you need to receive the generated proof via
getResponse
.const zkConnectResponse = zkConnect.getResponse();
This will return you a
ZkConnectResponse
which contains the user proof which certifies the user is a member of the requested group.
Well done! You’re halfway there! 💪
Now that you have the proof, you need to verify the proof in your backend to be sure that the proof is valid with respect to
the-merge-contributor
group. If yes, you will allow your user to register his email.
A general rule in web development is that frontend input cannot be trusted.
Once you have obtained the proof from the frontend, you should send it to the backend for verification.
In your back end, you need to check the following:
- The validity of the cryptographic proof
- If the proof corresponds to proof of membership in the requested group
If these two conditions are checked, you know that the proof was generated by a contributor to The Merge without knowing their identity. 🤯
First, you will need to import it:
yarn add @sismo-core/zk-connect-server
After importing, the first step is to create a
zkConnectConfig
for your server and initialize a new ZKConnect server instance with it:import { ZkConnect, ZkConnectServerConfig, DataRequest } from "@sismo-core/zk-connect-server";
const zkConnectConfig: ZkConnectServerConfig = {
appId: "0x8f347ca31790557391cec39b06f02dc2",
}
const zkConnect = ZkConnect(zkConnectConfig);
You can enable the devMode in the backend to only verify cryptographically the proof without checking the group.
In conclusion, the devMode allows to generate a cryptographically valid proof for whatever address added in the client config while checking that the proof is only cryptographically valid in the backend.
Always remember to have the devMode enabled in the frontend and the backend if you want to use it when prototyping.
import { ZkConnect, ZkConnectServerConfig, DataRequest } from "@sismo-core/zk-connect-server";
const zkConnectConfig: ZkConnectServerConfig = {
appId: "0x8f347ca31790557391cec39b06f02dc2",
devMode: {
enabled: true,
}
}
const zkConnect = ZkConnect(zkConnectConfig);
With this ZkConnect instance, you can call the verify function which takes the dataRequest for the group and the
zkConnectResponse
sent by the frontend:const THE_MERGE_CONTRIBUTOR_REQUEST = DataRequest({ groupId: "0x42c768bb8ae79e4c5c05d3b51a4ec74a",});
const { vaultId } = await zkConnect.verify(
zkConnectResponse,
{ dataRequest: THE_MERGE_CONTRIBUTOR_REQUEST }
);
By doing this, you'll be able to verify the validity of the proof for the requested group. The DataRequest is crucial here as far as users could gain unauthorized access by providing valid proof for any group.
If the proof and the requested group are valid, a vaultId is returned. If not, an error is received.
The vaultId corresponds to a unique identifier for the user Data Vault, the best part of it is that this vaultId is derived from the appId so it is impossible to compare two vault ids from two different apps implementing zkConnect.
A user has now the ability to authenticate himself by sharing only proof of private data he owns while not leaking any addresses or accounts where this data comes from. 🤘
You can now certify that the email sent to your API belongs to a contributor of The Merge without knowing which contributor. 🤯 You are now at 95% of the job! There is one last issue to resolve: what if one of your users adds multiple emails to gain access more than once?
As you saw, the verify function will return you the vaultId.
This vaultId can be used as a user identifier, it is deterministically generated based on the following elements:
- The vault used to generate the proof
- The app ID
This means that the vaultId will remain constant for a specific app if your user tries to prove something twice.
With this property, we can check if a user has already generated a proof to gain EthCC access.
To do so, we only need to store the vaultId with the added email, and every time a proof is sent to the back end, check that the vaultId returned is not already associated with an email address.
And that’s it! 💜
You are now able to obtain the email addresses of all contributors to The Merge and invite them to EthCC without breaching their privacy, thanks to zero-knowledge proofs.
If you chose to use Next.js, we recommended using the Vercel service for deployment. With Vercel you will deploy your frontend and your backend in two clicks.
Here's how to get started:
- 1.
- 2.Create a new project and import your zksub repository in Vercel

Import a Git repository
- 3.When your repository is linked to Vercel, you should see this page appear:

Configure your project
Click on "Deploy", and congratulations! Your app is now deployed at https://[Name of your app].vercel.app. 💪
As previously mentioned, all Data can be categorized into groups. This means you are now able to create apps where users can prove anything about their identities in a privacy-preserving manner. Get creative, as the possibilities are endless!
If you have any questions about integrating zkConnect, don’t hesitate to reach out. The team will be happy to answer any questions you may have.
Get involved in the Sismo community: