IDKit is our solution for integrating World ID. You can use the React SDK for a pre-built widget or the Javascript/Modile SDKs for a custom integration. Both options let you integrate permissionless.
If you want to familiarize yourself with the core concepts of World ID, check out this page .
Step 1: Create an app in the Developer Portal
Create your app in the Developer Portal . Keep these values:
Step 2: Generate an RP signature in your backend
Never generate RP signatures on the client and never expose your RP signing
key. If the key leaks, attackers can forge requests as your app.
app/api/rp-signature/route.ts
import { NextResponse } from "next/server" ;
import { signRequest } from "@worldcoin/idkit/signing" ;
export async function POST ( request : Request ) : Promise < Response > {
const { action } = await request . json ();
const signingKey = process . env . RP_SIGNING_KEY ! ;
const { sig , nonce , createdAt , expiresAt } = signRequest ( action , signingKey );
return NextResponse . json ({
sig ,
nonce ,
created_at: createdAt ,
expires_at: expiresAt ,
});
}
Step 3: Generate the connect URL and collect proof
action: Use one stable string per intent. Examples: "event_march_2026" or "claim".
Keep it identical in signing, request creation, and verify.
signal: Set this when you need to bind app context into the proof. Examples:
user ID, wallet address. Your backend should enforce the same value.
JavaScript
React
Swift
Kotlin
import { IDKit , orbLegacy } from "@worldcoin/idkit-core" ;
const rpSig = await fetch ( "/api/rp-signature" , {
method: "POST" ,
headers: { "content-type" : "application/json" },
body: JSON . stringify ({ action: "my-action" }),
}). then (( r ) => r . json ());
const request = await IDKit . request ({
app_id: "app_xxxxx" ,
action: "my-action" ,
rp_context: {
rp_id: "rp_xxxxx" ,
nonce: rpSig . nonce ,
created_at: rpSig . created_at ,
expires_at: rpSig . expires_at ,
signature: rpSig . sig ,
},
allow_legacy_proofs: true ,
environment: "production" ,
}). preset ( orbLegacy ());
const connectUrl = request . connectorURI ;
const response = await request . pollUntilCompletion ();
Step 4: Verify the proof in your backend
After successful completion, send the returned payload to your backend and
forward it directly to: POST https://developer.world.org/api/v4/verify/{rp_id}
Forward the IDKit result payload as-is. No field remapping is required.
app/api/verify-proof/route.ts
import { NextResponse } from "next/server" ;
import type { IDKitResult } from "@worldcoin/idkit" ;
export async function POST ( request : Request ) : Promise < Response > {
const { rp_id , devPortalPayload } = ( await request . json ()) as {
rp_id : string ;
devPortalPayload : IDKitResult ;
};
const response = await fetch (
`https://developer.world.org/api/v4/verify/ ${ rp_id } ` ,
{
method: "POST" ,
headers: { "content-type" : "application/json" },
body: JSON . stringify ( devPortalPayload ),
},
);
const payload = await response . json ();
return NextResponse . json ( payload , { status: response . status });
}
Next pages