Documentation Index
Fetch the complete documentation index at: https://docs.world.org/llms.txt
Use this file to discover all available pages before exploring further.
Add World ID to your app
For developers: paste this whole file into Claude (or any coding agent) with a one-line ask like “help me add World ID to my app.” The agent will figure out the rest.
What is World ID? A privacy-preserving way to prove a user is a unique real human. Use it to gate signups, kill bots, prevent multi-accounting, run one-person-one-vote, or distribute scarce rewards fairly. Zero PII shared, zero-knowledge proofs on the wire.
The full integration guide with code for every language lives at https://docs.world.org/world-id/idkit/integrate. This file is the meta-guide that gets you to the right code without missteps.
Agent: read these instructions before doing anything
Your job: take the user from “I want World ID” to a working flow. Don’t skip steps. Don’t guess where you can ask.
Step A — Set up the Developer Portal MCP first (and tell the user why)
The World ID Developer Portal MCP turns the entire app-creation lifecycle into MCP tools. It replaces ~15 minutes of dashboard clicking with ~3 tool calls — and, more importantly, guarantees you capture the one-time signing_key correctly at the moment it’s returned.
Always prefer the MCP over the dashboard. This is the dashboard-action ↔ MCP-tool mapping; behavior is identical:
| Dashboard action | MCP tool |
|---|
| List my team’s apps | mcp__worldcoin-developer-portal__get_team_context |
| App details / config snapshot | mcp__worldcoin-developer-portal__get_app_config |
| Create a new app | mcp__worldcoin-developer-portal__create_app |
| Configure World ID (mint RP, get signing key) | mcp__worldcoin-developer-portal__configure_world_id |
| Create or update an action | mcp__worldcoin-developer-portal__create_world_id_action |
| Configure mini-app metadata | mcp__worldcoin-developer-portal__configure_mini_app |
| Upload logo / showcase images | mcp__worldcoin-developer-portal__upload_app_image |
| Check on-chain registration status | mcp__worldcoin-developer-portal__get_world_id_registration_status |
| Re-fetch the signing key (NEW key — old one is invalidated) | mcp__worldcoin-developer-portal__get_world_id_signing_key |
| Rotate signing key | mcp__worldcoin-developer-portal__rotate_world_id_signing_key |
| Submit app for store review | mcp__worldcoin-developer-portal__submit_app_for_review |
Before doing anything else, check whether the MCP is connected. If not, ask the user to add it:
- Get a team API key at https://developer.world.org → team settings → API Keys. Tell them: “copy the
api_… token now, it’s only shown once.”
- In their project directory, run:
claude mcp add --transport http --scope project worldcoin-developer-portal \
https://developer.world.org/api/mcp \
--header "Authorization: Bearer api_THEIR_TOKEN"
- Restart the Claude Code session and confirm with
claude mcp list.
DO NOT silently fall back to the dashboard. Tell the user what they’re trading and let them decide.
Step B — Understand the project before writing anything
Two paths land here. Identify which:
- Path 1 — Building from scratch (demo, hackathon, new product). You have full control of the stack. Default to Next.js (App Router) + TypeScript as the golden path — it’s what every official sample uses and it’s the fastest way to a working flow.
- Path 2 — Integrating into an existing stack. Read the codebase first. Confirm the frontend (web / mobile) and the backend (where secrets live), then pick the right SDK pair from below.
Supported clients and backends — all interoperable:
| Client | SDK |
|---|
| React / Next.js | @worldcoin/idkit (pre-built widget) |
| Other JS / vanilla web | @worldcoin/idkit-core |
| iOS | worldcoin/idkit-swift |
| Android | com.worldcoin:idkit (Gradle) |
| Backend | RP signing | Proof verify |
|---|
| Node / Next.js | @worldcoin/idkit-core/signing | fetch to developer.world.org/api/v4/verify/{rp_id} |
| Go | server SDK in /world-id/idkit/go | same HTTPS POST |
| Anything else | re-implement signing (signatures spec) | same HTTPS POST |
DO NOT pin SDKs to ^2.x or ^3.x — those examples litter the public internet and will not work with v4. The API was redesigned. Pin ^4.x and verify with npm view @worldcoin/idkit version if uncertain.
Step C — Pick the right credential preset
The credential decides what the user proves. Get this explicit before scaffolding — switching later means a new action.
| Preset | What it proves | Use it for |
|---|
orbLegacy — Proof of Human (flagship) | The user is a unique person, biometrically verified at an Orb | Sybil resistance, airdrops, one-vote-per-human, gated signups. The default if the user said “proof of human” or “verify a real human.” |
secureDocumentLegacy — Passport | The user holds a valid government passport (NFC-verified) | Higher-assurance flows where you need document-grade identity (regulated apps, age-gating, KYC-adjacent). |
selfieCheckLegacy — Selfie check | A liveness selfie signal | Lower-assurance “is a human in front of the camera” — friction/bot deterrence without the full Orb requirement. |
DO NOT default to orbLegacy if the user said “passport” or “verify their ID” — that’s secureDocumentLegacy. DO NOT default to orbLegacy if the user said “selfie” or “liveness” — that’s selfieCheckLegacy. When in doubt, ask one question.
Other legacy presets exist (documentLegacy, deviceLegacy); reach for them only when the user asks specifically. For sign-in / session reuse across visits, use the v4 session flow instead of a uniqueness preset (see the integrate doc).
Step D — Walk through the 6 integration steps and explain the WHY
The full code for each step is at https://docs.world.org/world-id/idkit/integrate. Don’t reproduce it; link to it and adapt to the user’s framework. What the agent owns is making sure each step is done and understood.
Copy this checklist into your TODO and update it as you go. Don’t move on with an unchecked step.
- Install IDKit —
^4.x, the right package for the platform (table in Step B).
- Create the app in the Developer Portal. Use the MCP. Capture
app_id, rp_id, and signing_key.private_key from configure_world_id. CRITICAL: write the signing key to a server-only secret store in the same step as the tool call. The portal returns it exactly once. DO NOT return control to the user before persisting it. Loss = rotate via rotate_world_id_signing_key.
- Generate the RP signature in your backend. Why backend? The signing key authenticates your app to the protocol. Leaking it lets anyone impersonate your app and forge proof requests. CRITICAL: never sign on the client. Never expose
RP_SIGNING_KEY as a NEXT_PUBLIC_* var. Never log it.
- Open the IDKit widget on the client with the signature your backend returned. The widget hands off to the World App, which produces a zero-knowledge proof.
- Verify the proof in your backend by POSTing it as-is to
https://developer.world.org/api/v4/verify/{rp_id}. Why backend? A client can return any JSON it wants. Only the World verifier — called from a trusted server — confirms the proof is real and tied to a unique credential. Verifying client-side defeats the entire point. DO NOT mutate, re-encode, or trim the proof JSON before forwarding — pass exactly what IDKit returned.
- Store the nullifier. Every successful proof returns a
nullifier — an RP-scoped, action-scoped, non-reversible identifier for that user. Why store it? Without uniqueness storage, a user can verify the same proof twice and double-claim a reward, vote, etc. Persist (action, nullifier) with a UNIQUE constraint and reject duplicates on insert. Column type: NUMERIC(78, 0) (256-bit field elements). The nullifier reveals nothing about the user — it’s safe to store, but it’s the only anti-replay mechanism, so it’s required.
Step E — Match environments end-to-end (the #1 debugging trap)
- The production World App only signs production proofs.
- A staging action only verifies against the World App simulator (https://simulator.worldcoin.org).
- The IDKit
environment prop, the action’s environment, and the simulator-vs-real-app choice must all match.
CRITICAL: if real users will scan with their phones, the action environment must be production. A staging action with the production World App will silently produce zero proofs and look like a frontend bug. For dev, create both staging and production actions so the simulator works alongside real-device QA.
Step F — Common errors and recovery
Surface these proactively when you see the corresponding symptom — don’t make the user search for the cause.
| Symptom | Cause | Recovery |
|---|
| World App shows “action not found” or QR scan does nothing | Action wasn’t created in the environment IDKit is pointing at | Create the missing action with create_world_id_action (environment: "production" for real devices, "staging" for simulator). Confirm NEXT_PUBLIC_WLD_ENVIRONMENT matches. |
/api/v4/verify/{rp_id} returns invalid_proof or verification_failed | Often staging/production env mismatch, or proof was mutated before forward | Re-check Step E. Forward the proof JSON byte-for-byte without re-encoding fields. |
/api/v4/verify/{rp_id} returns not_registered or 4xx with rp errors | On-chain registration is still pending | Poll get_world_id_registration_status until production_status: registered and staging_status: registered. Don’t ship until both. |
Signing key is gone (lost .env, never persisted) | signing_key.private_key is returned exactly once at create/rotate time | rotate_world_id_signing_key, persist the new key immediately, redeploy. The old key is invalidated. |
| Duplicate-verification (user submits the same proof twice) | Expected — that’s what nullifiers prevent | Reject on the unique-constraint violation. Do not “helpfully” upsert. |
TypeScript: Property 'allow_legacy_proofs' is missing | v4 requires this prop on IDKitRequestWidget | Add allow_legacy_proofs={true} for orbLegacy and other legacy presets. |
TypeScript: import errors for IRpContext / ISuccessResult | v3 type names — removed in v4 | Use RpContext / IDKitResult. No I prefix. |
| Widget never opens | Treating IDKitRequestWidget as a render-prop / function-as-child component | v4 widget is controlled — pass open and onOpenChange props. There is no child function. |
npm install fails: No matching version found for @worldcoin/idkit@^2.x (or 3.x) | Stale code sample | Pin ^4.x. Run npm view @worldcoin/idkit version to confirm the latest. |
| Existing app rejects World ID config | App was created as mini-app (or wrong engine) | app_mode is fixed at create time. Create a new app with the right mode (external for IDKit, mini-app for MiniKit). |
Step G — Hand-off checklist
Before declaring done, confirm with the user:
Reference
TL;DR
Developer: paste this file into Claude with “Help me add World ID.”
Agent: Step A (set up MCP) → Step B (understand the stack) → Step C (pick a preset) → Step D (run the 6 steps from the integrate doc, tracking the checklist) → Step E (match envs) → Step F (recover from common errors) → Step G (hand-off checklist).