Commands

Verify

The verify command lets you use incognito actions inside of your mini app. Incognito actions are a primitive of World ID and allow you to gate functionality behind a unique human check.

To use incognito actions, first create one in the Developer Portal.

Using the command

Async handlersEvent listeners

Sending the command & handling the response

MiniKit uses a slightly different input payload than IDKit. We do not need to pass in the app_id.

export type VerifyCommandInput = {
	action: string
	signal?: string
	verification_level?: VerificationLevel // Default: Orb
}
type MiniAppVerifyActionSuccessPayload = {
	status: 'success'
	proof: string
	merkle_root: string
	nullifier_hash: string
	verification_level: VerificationLevel
	version: number
}

app/page.tsx

import { MiniKit, VerifyCommandInput, VerificationLevel, ISuccessResult } from '@worldcoin/minikit-js'

const verifyPayload: VerifyCommandInput = {
	action: 'voting-action', // This is your action ID from the Developer Portal
	signal: '0x12312', // Optional additional data
	verification_level: VerificationLevel.Orb, // Orb | Device
}

const handleVerify = async () => {
	if (!MiniKit.isInstalled()) {
		return
	}
	// World App will open a drawer prompting the user to confirm the operation, promise is resolved once user confirms or cancels
	const {finalPayload} = await MiniKit.commandsAsync.verify(verifyPayload)
		if (finalPayload.status === 'error') {
			return console.log('Error payload', finalPayload)
		}

		// Verify the proof in the backend
		const verifyResponse = await fetch('/api/verify', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
			},
			body: JSON.stringify({
			payload: finalPayload as ISuccessResult, // Parses only the fields we need to verify
			action: 'voting-action',
			signal: '0x12312', // Optional
		}),
	})

	// TODO: Handle Success!
	const verifyResponseJson = await verifyResponse.json()
	if (verifyResponseJson.status === 200) {
		console.log('Verification success!')
	}
}

Verifying the proof

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.

Successful responses will return a MiniAppVerifyActionSuccessPayload.

type MiniAppVerifyActionSuccessPayload = {
	status: 'success'
	proof: string
	merkle_root: string
	nullifier_hash: string
	verification_level: VerificationLevel
	version: number
}

To verify the proof, you will need to make a backend route.

app/api/verify/route.ts

import { NextRequest, NextResponse } from 'next/server'
import { verifyCloudProof, IVerifyResponse, ISuccessResult } from '@worldcoin/minikit-js'

interface IRequestPayload {
	payload: ISuccessResult
	action: string
	signal: string | undefined
}

export async function POST(req: NextRequest) {
	const { payload, action, signal } = (await req.json()) as IRequestPayload
	const app_id = process.env.APP_ID as `app_${string}`
	const verifyRes = (await verifyCloudProof(payload, app_id, action, signal)) as IVerifyResponse // Wrapper on this

	if (verifyRes.success) {
		// This is where you should perform backend actions if the verification succeeds
		// Such as, setting a user as "verified" in a database
		return NextResponse.json({ verifyRes, status: 200 })
	} else {
		// This is where you should handle errors from the World ID /verify endpoint.
		// Usually these errors are due to a user having already verified.
		return NextResponse.json({ verifyRes, status: 400 })
	}
}

Success Result on World App

If implemented correctly, the user will see the following drawer on World App.