Incognito Actions

Cloud Incognito Actions

IDKit is required in your app's frontend for Incognito Actions, and the zero-knowledge proof received from the user will be verified via the Developer Portal API in your backend.

Creating actions

Create an action for your app in the Developer Portal. You must provide the following values:

  • Action Name: The stringified action to be taken by the user.
  • Description: This is shown to your user in the World app as they sign with their World ID. Make sure to fully describe the exact action the user is taking.
  • Max Verifications: The number of times a user can take this action. A value of 0 indicates that unlimited verifications can take place.

An action scopes uniqueness for users, which means users will always generate the same ID (nullifier hash) when performing the same action. Cloud actions natively handle sybil-resistance with a limit set in the Developer Portal.

Installing IDKit

The JS package can be included in your project either as a module (which supports tree shaking to reduce bundle size) or you can add the script directly to your website.

Install IDKit

npm install @worldcoin/idkit

Usage

Import and render IDKit. You'll want to do this on the screen where the user executes the protected action (e.g. before they click "Claim airdrop" or "Vote on proposal").

import { IDKitWidget, VerificationLevel } from '@worldcoin/idkit'

<IDKitWidget
	app_id="app_GBkZ1KlVUdFTjeMXKlVUdFT" // obtained from the Developer Portal
	action="vote_1" // this is your action id from the Developer Portal
	onSuccess={onSuccess} // callback when the modal is closed
	handleVerify={handleVerify} // optional callback when the proof is received
	verification_level={VerificationLevel.Device}
>
	{({ open }) => <button onClick={open}>Verify with World ID</button>}
</IDKitWidget>

More configuration options can be found in the IDKit reference.

When a user clicks the button, the IDKit modal will open and prompt them to scan a QR code and verify with World ID. Once this proof is received, the optional handleVerify callback is called immediately and the onSuccess callback will be called when the modal is closed. One of these callbacks should begin the process of verifying the proof.

IDKit with Dynamic Actions

To accommodate dynamic content, actions can also be created at the time a user completes a World ID verification. Simply pass the desired action and action_description values in IDKit's parameters. A new action will automatically be created and tracked, and will appear the next time you log into the Developer Portal.

As an example, using IDKit with Dynamic Actions may look like this:

const getUserChoice = userId => {
	const choice = userChoices['userId']
	return choice
}

return (
    <IDKitWidget
        {/* ... */}
        action={getUserChoice(userId)}
        action_description="verify for an action"
        {/* ... */}
    >
    </IDKitWidget>
)

Response

Upon successful completion of the World ID flow, you will receive a response object. This response object of type ISuccessResult has the following attributes. Normally, you will forward these parameters to your backend for verification.

ISuccessResult

{
	"merkle_root": "0x1f38b57f3bdf96f05ea62fa68814871bf0ca8ce4dbe073d8497d5a6b0a53e5e0",
	"nullifier_hash": "0x0339861e70a9bdb6b01a88c7534a3332db915d3d06511b79a5724221a6958fbe",
	"proof": "0x063942fd7ea1616f17787d2e3374c1826ebcd2d41d2394...",
	"verification_level": "orb"
}
  • Name
    merkle_root
    Type
    string
    Description

    This is the hash pointer to the root of the Merkle tree that proves membership of the user's identity in the list of identities verified by the Orb.

  • Name
    nullifier_hash
    Type
    string
    Description

    The unique identifier for this combination of user, app, and action.

  • Name
    proof
    Type
    string
    Description

    The Zero-knowledge proof of the verification.

  • Name
    verification_level
    Type
    "orb" | "device"
    Description

    Either orb or device. Returns the verification_level used to generate the proof.

Verifying Proofs

This section describes how to verify proofs via the Developer Portal API.

You should pass the proof to your backend when verifying proofs via the API. Users can manipulate information in the frontend, so the proof must be verified in a trusted environment.

Your backend should receive the proof, merkle_root, nullifier_hash, and verification_level returned by IDKit, as well as the signal that was input into IDKit, and send it to the Developer Portal API for verification. The action ID should be accessible in your backend as an environment variable unless using Dynamic Actions, in which case you should pass the action ID to the backend with the proof and signal. The Developer Portal API will return a 200 response if the proof is valid, and a 400 response with extra error detail if the proof is invalid. After performing your own backend actions based on this result, you then pass the success or error messages back to your frontend.

pages/api/verify.ts

export type VerifyReply = {
	code: string
	detail?: string
}

export default function handler(req: NextApiRequest, res: NextApiResponse<VerifyReply>) {
	const reqBody = {
		merkle_root: req.body.merkle_root,
		nullifier_hash: req.body.nullifier_hash,
		proof: req.body.proof,
		verification_level: req.body.verification_level,
		action: process.env.NEXT_PUBLIC_WLD_ACTION_ID,
		signal: req.body.signal ?? '', // if we don't have a signal, use the empty string
	}
	fetch(`https://developer.worldcoin.org/api/v1/verify/${process.env.NEXT_PUBLIC_WLD_APP_ID}`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
		},
		body: JSON.stringify(reqBody),
	}).then(verifyRes => {
		verifyRes.json().then(wldResponse => {
			if (verifyRes.status == 200) {
				// this is where you should perform backend actions based on the verified credential
				// i.e. setting a user as "verified" in a database
				res.status(verifyRes.status).send({ code: 'success' })
			} else {
				// return the error code and detail from the World ID /verify endpoint to our frontend
				res.status(verifyRes.status).send({
					code: wldResponse.code,
					detail: wldResponse.detail,
				})
			}
		})
	})
}

Post-Verification

If handleVerify does not throw an error, the user will see a success state and the onSuccess callback will be called when the modal is closed. The onSuccess callback should redirect a user to a success page, or perform any other actions you want to take after a user has been verified.

pages/index.tsx

const onSuccess = (result: ISuccessResult) => {
	// This is where you should perform frontend actions once a user has been verified
	window.alert(
		`Successfully verified with World ID!
    Your nullifier hash is: ` + result.nullifier_hash
	)
}

For more information on configuration, see the IDKit and Cloud API reference pages.