From e2fb724cdb91d0910c70344ea5c422dc05595014 Mon Sep 17 00:00:00 2001
From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com>
Date: Fri, 6 Feb 2026 23:35:17 +0200
Subject: [PATCH] docs: IDKit v4 preview
Add unlisted documentation pages for IDKit v4 SDK and the v4 verify API endpoint.
---
world-id/id/idkit-v4-preview.mdx | 282 +++++++++++++++++++++++++++++++
world-id/reference/api-v4.mdx | 243 ++++++++++++++++++++++++++
2 files changed, 525 insertions(+)
create mode 100644 world-id/id/idkit-v4-preview.mdx
create mode 100644 world-id/reference/api-v4.mdx
diff --git a/world-id/id/idkit-v4-preview.mdx b/world-id/id/idkit-v4-preview.mdx
new file mode 100644
index 0000000..b2d55af
--- /dev/null
+++ b/world-id/id/idkit-v4-preview.mdx
@@ -0,0 +1,282 @@
+---
+title: "IDKit v4 (Preview)"
+description: "Preview guide for @worldcoin/idkit-core v4 — the new pure TypeScript SDK for World ID verification."
+"og:image": "/images/docs/docs-meta.png"
+"twitter:image": "/images/docs/docs-meta.png"
+---
+
+
+ IDKit v4 is currently in **preview**. APIs may change before the stable
+ release. Use `@worldcoin/idkit-core@^4.0` for early access.
+
+
+## Install
+
+
+```bash npm
+npm install @worldcoin/idkit-core
+```
+
+```bash pnpm
+pnpm add @worldcoin/idkit-core
+```
+
+```bash yarn
+yarn add @worldcoin/idkit-core
+```
+
+
+
+## Prerequisites
+
+Before writing code you need:
+
+1. An **app ID** (`app_...`) from the [Developer Portal](https://developer.worldcoin.org).
+2. An **RP ID** (`rp_...`) and **signing key** — obtained when you register your Relying Party in the Developer Portal.
+3. A backend endpoint that generates RP signatures (see [Backend setup](#backend-setup)).
+
+## Quick start (browser)
+
+```typescript
+import { IDKit, orbLegacy } from "@worldcoin/idkit-core";
+
+// 1. Initialize (call once on page load)
+await IDKit.init();
+
+// 2. Fetch an RP signature from your backend
+const rpSig = await fetch("/api/rp-signature", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ action: "my-action" }),
+}).then((r) => r.json());
+
+// 3. Create a verification request
+const request = await IDKit.request({
+ app_id: "app_staging_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,
+}).preset(orbLegacy({ signal: "user-123" }));
+
+// 4. Display QR code for users to scan with World App
+console.log("Scan this:", request.connectorURI);
+
+// 5. Wait for the proof
+const result = await request.pollForUpdates({
+ pollInterval: 2000, // ms between polls
+ timeout: 120_000, // 2 minute timeout
+});
+
+// 6. Send result directly to the Developer Portal v4 verify endpoint
+const verification = await fetch("/api/verify-proof", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(result),
+});
+```
+
+
+ Use `IDKit.init()` in the browser and `IDKit.initServer()` in Node.js. Both
+ are safe to call multiple times — initialization only happens once.
+
+
+## Backend setup
+
+Your backend needs two endpoints: one to generate RP signatures, and one to verify proofs.
+
+### Generate RP signatures
+
+The `signRequest` function must run server-side — it requires your RP signing key.
+
+```typescript
+import { IDKit, signRequest } from "@worldcoin/idkit-core";
+
+// Initialize for Node.js (call once at startup)
+await IDKit.initServer();
+
+const SIGNING_KEY = process.env.RP_SIGNING_KEY; // 32-byte hex private key
+
+app.post("/api/rp-signature", (req, res) => {
+ const { action } = req.body;
+ const sig = signRequest(action, SIGNING_KEY);
+
+ res.json({
+ sig: sig.sig,
+ nonce: sig.nonce,
+ created_at: Number(sig.createdAt),
+ expires_at: Number(sig.expiresAt),
+ });
+});
+```
+
+
+ Never expose your RP signing key to client-side code. `signRequest()` will
+ throw if called outside a server environment.
+
+
+### Verify proofs
+
+The result object returned by `pollForUpdates()` is shaped exactly as the Developer Portal's [`/v4/verify`](/world-id/reference/api-v4) endpoint expects — just forward it directly:
+
+```typescript
+app.post("/api/verify-proof", async (req, res) => {
+ const proof = req.body;
+
+ const response = await fetch(
+ `https://developer.world.org/api/v4/verify/${RP_ID}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(proof),
+ },
+ );
+
+ const result = await response.json();
+ res.status(response.ok ? 200 : 400).json(result);
+});
+```
+
+## Presets
+
+Presets are the simplest way to create a request. Each preset configures the credential type and handles both v4 and v3 proof formats.
+
+| Preset | Credential | Function |
+| ------------------------ | ----------------- | ----------------------------------- |
+| Orb (Legacy) | `orb` | `orbLegacy({ signal? })` |
+| Secure Document (Legacy) | `secure_document` | `secureDocumentLegacy({ signal? })` |
+| Document (Legacy) | `document` | `documentLegacy({ signal? })` |
+
+```typescript
+import { IDKit, orbLegacy, secureDocumentLegacy } from "@worldcoin/idkit-core";
+
+// Orb verification with a signal
+const request = await IDKit.request({ ...config }).preset(
+ orbLegacy({ signal: "user-123" }),
+);
+
+// Secure document verification
+const request2 = await IDKit.request({ ...config }).preset(
+ secureDocumentLegacy(),
+);
+```
+
+## Request configuration
+
+
+
+ Your application ID from the Developer Portal.
+
+
+ The action identifier. Should match what's configured in the Developer
+ Portal.
+
+
+ RP signature context generated by your backend via `signRequest()`. Contains
+ `rp_id`, `nonce`, `created_at`, `expires_at`, and `signature`.
+
+
+ Set to `true` to accept World ID v3 proofs alongside v4. Set to `false` for
+ v4-only.
+
+
+ Human-readable description shown to users in World App. Recommended for
+ dynamically created actions.
+
+
+ Override the default bridge URL. Only change this if running your own bridge
+ service.
+
+
+
+## Polling for results
+
+Once you have a request, display `connectorURI` as a QR code and poll for the proof:
+
+```typescript
+// request.connectorURI — show this as a QR code
+// request.requestId — unique ID for this verification
+
+// Option A: Poll continuously (recommended)
+const result = await request.pollForUpdates({
+ pollInterval: 2000, // default: 1000ms
+ timeout: 120_000, // default: 300_000ms (5 min)
+ signal: abortController.signal, // optional cancellation
+});
+
+// Option B: Manual polling
+const status = await request.pollOnce();
+// status.type: "waiting_for_connection" | "awaiting_confirmation" | "confirmed" | "failed"
+if (status.type === "confirmed") {
+ console.log(status.result);
+}
+```
+
+## Response format
+
+The result from `pollForUpdates()` can be forwarded directly to the Developer Portal [`/v4/verify`](/world-id/reference/api-v4) endpoint — no transformation needed.
+
+The response shape depends on which World ID protocol version the user's World App uses.
+
+### World ID v3 vs v4
+
+World ID **v3** is the current live protocol. World ID **v4** is the new protocol shipping with this SDK. During the preview period, you will receive **v3 proofs** since World ID v4 has not launched yet. Once v4 rolls out, users with updated World Apps will produce v4 proofs instead.
+
+Set `allow_legacy_proofs: true` so your app works with both — the Developer Portal `/v4/verify` endpoint handles either format transparently.
+
+### V3 response (current — World ID 3.0)
+
+```typescript
+interface IDKitResultV3 {
+ protocol_version: "3.0";
+ nonce: string;
+ action?: string;
+ responses: ResponseItemV3[];
+}
+
+interface ResponseItemV3 {
+ identifier: string; // e.g. "orb", "face"
+ proof: string; // ABI-encoded proof (hex)
+ merkle_root: string; // Merkle root (hex)
+ nullifier: string; // nullifier hash (hex)
+ signal_hash?: string; // included if signal was provided
+}
+```
+
+### V4 response (upcoming — World ID 4.0)
+
+```typescript
+interface IDKitResultV4 {
+ protocol_version: "4.0";
+ nonce: string;
+ action: string;
+ responses: ResponseItemV4[];
+}
+
+interface ResponseItemV4 {
+ identifier: string; // e.g. "orb", "face", "document"
+ proof: string[]; // compressed Groth16 proof + Merkle root
+ nullifier: string; // RP-scoped nullifier (hex)
+ signal_hash?: string; // included if signal was provided
+ issuer_schema_id: number; // 1=orb, 2=face, 3=secure_document, 4=document, 5=device
+ expires_at_min: number; // credential expiration (unix seconds)
+}
+```
+
+You can check `protocol_version` on the result to determine which format you received.
+
+
+ The `signal_hash` field is returned as a convenience — it's the hash of the signal you
+ provided in the preset. You can also compute it yourself with `hashSignal()`:
+
+```typescript
+import { hashSignal } from "@worldcoin/idkit-core";
+const hash = hashSignal("user-123"); // 0x-prefixed hex string
+```
+
+
diff --git a/world-id/reference/api-v4.mdx b/world-id/reference/api-v4.mdx
new file mode 100644
index 0000000..32f006b
--- /dev/null
+++ b/world-id/reference/api-v4.mdx
@@ -0,0 +1,243 @@
+---
+title: "API Reference (v4 Preview)"
+description: "Developer Portal v4 API: verify World ID proofs with support for both v3 (cloud) and v4 (on-chain) proof formats."
+"og:image": "/images/docs/docs-meta.png"
+"twitter:image": "/images/docs/docs-meta.png"
+---
+
+
+ The v4 verify endpoint is in **preview**. It is the verification backend for
+ [IDKit v4](/world-id/id/idkit-v4-preview).
+
+
+## Base URL
+
+```
+https://developer.world.org
+```
+
+
+ All requests must include `Content-Type: application/json` and a valid JSON body.
+
+
+## Verify Proof
+
+
+ https://developer.world.org/api/v4/verify/\{rp_id\}
+
+
+Verifies a World ID proof returned by IDKit v4. Accepts both `rp_id` (`rp_xxx`) and `app_id` (`app_xxx`) as the route parameter.
+
+The request body from `pollForUpdates()` can be forwarded directly to this endpoint — no transformation needed.
+
+This endpoint handles both World ID v3 (cloud) and v4 (on-chain) proof formats. During the preview period, users will produce **v3 proofs** since World ID v4 has not launched yet. The endpoint verifies either format transparently.
+
+### Request Body
+
+
+ The protocol version. Must be `"3.0"` or `"4.0"`. IDKit sets this automatically.
+
+
+
+ The nonce used in the RP signature, as returned by IDKit.
+
+
+
+ The action identifier. Must match the action passed to `IDKit.request()`.
+
+
+
+ Human-readable description of the action. Used when creating the action for the first time.
+
+
+
+ Array of proof response items (at least one). The shape of each item depends on `protocol_version`.
+
+
+#### Response item fields (protocol 3.0)
+
+
+ Credential type: `"orb"`, `"device"`, or `"face"`.
+
+
+
+ ABI-encoded zero-knowledge proof (hex string).
+
+
+
+ Merkle root hash (hex string).
+
+
+
+ Nullifier hash (hex string, optional `0x` prefix).
+
+
+
+ Hash of the signal. Defaults to the keccak256 hash of an empty string.
+
+
+
+ Maximum age of the Merkle root in seconds. Range: 3600 (1 hour) to 604800 (7 days).
+
+
+#### Response item fields (protocol 4.0)
+
+
+ Credential type: `"orb"`, `"face"`, `"secure_document"`, `"document"`, or `"device"`.
+
+
+
+ Array of exactly 5 hex strings: 4 compressed Groth16 proof elements + Merkle root.
+
+
+
+ RP-scoped nullifier (hex string, optional `0x` prefix).
+
+
+
+ Credential issuer schema ID: 1=orb, 2=face, 3=secure_document, 4=document, 5=device.
+
+
+
+ Minimum credential expiration timestamp (unix seconds).
+
+
+
+ Hash of the signal. Defaults to `"0x0"`.
+
+
+### Request Examples
+
+
+```bash cURL
+curl -X POST "https://developer.world.org/api/v4/verify/rp_xxxxx" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "protocol_version": "3.0",
+ "nonce": "0x1234...",
+ "action": "my-action",
+ "responses": [
+ {
+ "identifier": "orb",
+ "proof": "0x1aa8b8f3b2d2de5ff452c0e1a83e29d6bf46fb83ef35dc5957121ff3d3698a11...",
+ "merkle_root": "0x2264a66d162d7893e12ea8e3c072c51e785bc085ad655f64c10c1a61e00f0bc2",
+ "nullifier": "0x2bf8406809dcefb1486dadc96c0a897db9bab002053054cf64272db512c6fbd8",
+ "signal_hash": "0x00c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4"
+ }
+ ]
+ }'
+```
+
+```javascript JavaScript
+const result = await request.pollForUpdates(); // IDKit result
+
+const response = await fetch(
+ `https://developer.world.org/api/v4/verify/${RP_ID}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(result), // forward directly
+ },
+);
+```
+
+
+### Possible Responses
+
+- `200 OK` — At least one proof was successfully verified.
+- `400 Bad Request` — All proofs failed verification, or the user has already verified for this action.
+- `404 Not Found` — App not found or no longer active.
+
+### Response Examples
+
+
+
+ ```json
+ {
+ "success": true,
+ "action": "my-action",
+ "nullifier": "0x2bf8406809dcefb1486dadc96c0a897db9bab002053054cf64272db512c6fbd8",
+ "created_at": "2025-02-18T11:20:39.530041+00:00",
+ "environment": "production",
+ "results": [
+ {
+ "identifier": "orb",
+ "success": true,
+ "nullifier": "0x2bf8406809dcefb1486dadc96c0a897db9bab002053054cf64272db512c6fbd8"
+ }
+ ],
+ "message": "Proof verified successfully"
+ }
+ ```
+
+
+ ```json
+ {
+ "success": false,
+ "code": "all_verifications_failed",
+ "detail": "All proof verifications failed.",
+ "results": [
+ {
+ "identifier": "orb",
+ "success": false,
+ "code": "verification_error",
+ "detail": "On-chain proof verification failed."
+ }
+ ]
+ }
+ ```
+
+
+ ```json
+ {
+ "success": false,
+ "code": "max_verifications_reached",
+ "detail": "This person has already verified for this action."
+ }
+ ```
+
+
+ ```json
+ {
+ "success": false,
+ "code": "app_not_migrated",
+ "detail": "This app has not been migrated to World ID 4.0. Please use the v2 verify endpoint."
+ }
+ ```
+
+
+ ```json
+ {
+ "success": false,
+ "code": "not_found",
+ "detail": "App not found. App may be no longer active."
+ }
+ ```
+
+
+
+### Response Fields
+
+
+ Whether at least one proof was verified successfully.
+
+
+
+ The action identifier (returned on success).
+
+
+
+ The nullifier from the first successful proof (returned on success). Use this to track unique users per action.
+
+
+
+ ISO 8601 timestamp of when the nullifier was recorded.
+
+
+
+ `"production"` or `"staging"`. In staging, nullifier reuse is allowed.
+
+
+
+ Per-response verification results. Each entry includes `identifier`, `success`, and optionally `nullifier` (on success) or `code`/`detail` (on failure).
+