Skip to content
4 changes: 2 additions & 2 deletions world-id/4-0-migration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ proofs. At the app's cutover, it begins accepting only v4 proofs.

**Old Contract - Disable minting here:**

```jsx Mint.sol
```solidity Mint.sol
mapping(uint256 => bool) internal oldNullifierHashes;
mapping(address => bool) public oldHasMinted;

Expand All @@ -136,7 +136,7 @@ function mint(){

**New Contract - Check both old and new nullifiers:**

```ts Mintv4.sol
```solidity Mintv4.sol
mapping(uint256 => bool) internal nullifierHashes;
mapping(address => bool) public hasMinted;

Expand Down
4 changes: 2 additions & 2 deletions world-id/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Other legacy presets exist (`documentLegacy`, `deviceLegacy`); reach for them on
### Selfie Check

For repeated Selfie Check verification, use `IDKit.createSession` followed by
`IDKit.proveSession` with `.preset(selfieCheck())`.
`IDKit.proveSession` with `.constraints(CredentialRequest("selfie"))`.
Save the verified `session_id` against the application account
and require that same session on later checks; enforce per-proof replay protection.

Expand All @@ -132,7 +132,7 @@ The full code for each step is at [https://docs.world.org/world-id/idkit/integra
The checklist below describes uniqueness requests. For session integrations,
adapt it using `/world-id/idkit/session-proofs`: omit action setup and omit the
action from RP signing,
use `IDKitSessionWidget` or the session builders with presets, and replace
use `IDKitSessionWidget` with a `constraints` prop or session builders with `.constraints(...)`, and replace
uniqueness-nullifier storage with verified account-to-session binding and
per-proof replay protection. Test both creation and proving the saved session.

Expand Down
2 changes: 1 addition & 1 deletion world-id/credentials/1.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ This credential implements the following attributes beyond the defaults in the [
</tr>
<tr>
<td className="whitespace-nowrap">
<code>associated_data_hash</code>
<code>associated_data_commitment</code>
</td>
<td>
The PoH credential has no associated data, so this field is always{" "}
Expand Down
4 changes: 2 additions & 2 deletions world-id/credentials/9303.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { CredentialHero } from "/snippets/credential-hero.jsx";

## Introduction

The NFC Credential represents a unique government-issued document. It supports passports and eIDs. Availability varies by country and continues to expand over time. An NFC Credential is **guaranteed to be issued to a single World ID per unique document**. In addition to ICAO-9303 compliant documents (such as passports or eIDs), the Japanese [My Number Card](https://en.wikipedia.org/wiki/My_Number_Card) (MNC) is also supported. The MNC flow uses different enrollment handling internally, but it issues the same credential.
The NFC Credential represents a unique government-issued document. It supports passports and eIDs. Availability varies by country and continues to expand over time. An NFC Credential is **guaranteed to be issued to a single World ID per unique document**. The Japanese [My Number Card](https://en.wikipedia.org/wiki/My_Number_Card) (MNC) has its own issuer schema ID, `9310`, and its own enrollment handling, distinct from passport/eID (`9303`), but it still counts as the user's one NFC credential. Request MNC and passport/eID as alternatives, never together.

## Use Cases

Expand Down Expand Up @@ -98,7 +98,7 @@ This credential implements the following attributes beyond the defaults in the [
</tbody>
</table>

In addition, the credential implements the following claims:
The following shared issuer claim definitions cover passport/eID and MNC enrollment. MNC-specific entries describe the MNC flow; they do not mean a passport/eID request accepts MNC. In IDKit, request passport/eID with `passport` (schema `9303`) and MNC with `mnc` (schema `9310`); to accept either, use `any(CredentialRequest("passport"), CredentialRequest("mnc"))`.

### Claim 0 - Authentication Claim

Expand Down
8 changes: 6 additions & 2 deletions world-id/from-idkit-standalone.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,23 @@ await fetch("/api/verify-proof", {

## Credential Mapping

Standalone used `verification_level` to choose what the user proves. In `idkit-core`, use a legacy preset instead. These presets are drop-in replacements for the old verification levels.
Standalone used `verification_level` to choose what the user proves. In `idkit-core`, use a legacy preset instead. These presets only return World ID 3.0 proofs — a compatibility step, not the final migration target.

<Note type="info">
Legacy presets return the maximum credential a user has. For example, if a user has an Orb credential but you request `documentLegacy`, they verify with their Orb credential.
</Note>

| Standalone | `idkit-core` preset |
| Standalone | `idkit-core` preset (compatibility-only) |
| --- | --- |
| `verification_level: "orb"` | `orbLegacy({ signal })` |
| `verification_level: "secure_document"` | `secureDocumentLegacy({ signal })` |
| `verification_level: "document"` | `documentLegacy({ signal })` |
| `verification_level: "device"` | `deviceLegacy({ signal })` |

<Note>
For World ID 4.0, use the v4-native presets: `proofOfHuman` (replaces `orbLegacy`), `passport` (NFC passports/eIDs), or `mnc` (Japanese My Number Card). They always keep World ID 3.0 fallback on, regardless of `allow_legacy_proofs`. A single preset narrows a `document` integration, since `documentLegacy` accepted Orb, passport/eID, and MNC holders alike; to keep all of them, use `.constraints(any(CredentialRequest("proof_of_human"), CredentialRequest("passport"), CredentialRequest("mnc")))`. Constraint requests have no credential-specific 3.0 fallback (any 3.0 fallback is Device-level), so use them with `allow_legacy_proofs: false`, which is also how you drop World ID 3.0 entirely. See [Configure Credentials](/world-id/idkit/credentials).
</Note>

```ts
import {
IDKit,
Expand Down
48 changes: 43 additions & 5 deletions world-id/idkit/credentials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A [Credential](https://docs.rs/world-id-primitives/latest/world_id_primitives/cr
| --- | --- | --- |
| Proof of Human | The user is a unique human, backed by anonymous biometric verification by the Orb. | `proofOfHuman` |
| Passport | The user holds a verified NFC passport credential. | `passport` |
| My Number Card | The user holds a verified Japanese My Number Card (MNC) credential. | `mnc` |
| Selfie Check | A medium-assurance biometric credential using the device camera for liveness and facial similarity. | `selfieCheck` |
| Identity Check | An attestation that a document-backed property about the user matches your requested attributes. | `identityCheck` with attributes like `minimum_age`, `nationality`, or `document_type`. |

Expand All @@ -25,7 +26,7 @@ You can also add a liveness check to any preset with `require_user_presence`.

# SDK presets

Preset helpers cover common credential requests: `proofOfHuman`, `passport`, `selfieCheck`, `identityCheck`, and the legacy presets.
Preset helpers cover common credential requests: `proofOfHuman`, `passport`, `mnc`, `selfieCheck`, `identityCheck`, and the legacy presets.

The snippets below assume `rp_context` was already generated and signed by your backend. See [Integrate IDKit](/world-id/idkit/integrate) for the full RP-signing flow.

Expand Down Expand Up @@ -103,22 +104,59 @@ const preset = passport({ signal: "user-123" });
```
</CodeGroup>

## My Number Card

Use `mnc` for the current World ID 4.0 My Number Card (MNC) credential. The preset also includes legacy document fallback for users without a World ID 4.0 proof.

<CodeGroup title="My Number Card">
```typescript title="JavaScript"
import { IDKit, mnc } from "@worldcoin/idkit-core";

const preset = mnc({ signal: "user-123" });

const request = await IDKit.request({
app_id: "app_xxxxx",
action: "my-action",
rp_context,
allow_legacy_proofs: true,
}).preset(preset);
```

```tsx title="React"
import { IDKitRequestWidget, mnc } from "@worldcoin/idkit";

const preset = mnc({ signal: "user-123" });

<IDKitRequestWidget
open={open}
onOpenChange={setOpen}
app_id="app_xxxxx"
action="my-action"
rp_context={rpContext}
allow_legacy_proofs={true}
preset={preset}
handleVerify={handleVerify}
onSuccess={(result) => { /* ... */ }}
/>;
```
</CodeGroup>

## Selfie Check

Use `selfieCheck()` with [session proofs](/world-id/idkit/session-proofs) to verify
Use `CredentialRequest("selfie")` with [session proofs](/world-id/idkit/session-proofs) to verify
users when they first arrive and when they return. Create a session once, save its
verified `session_id`, then prove that same session on later checks.

Anyone with World ID App can complete the flow—no Orb or document credential is
required.

```typescript
import { IDKit, selfieCheck } from "@worldcoin/idkit-core";
import { IDKit, CredentialRequest } from "@worldcoin/idkit-core";

const request = await IDKit.createSession({
app_id: "app_xxxxx",
rp_context,
}).preset(selfieCheck());
}).constraints(CredentialRequest("selfie"));
```

Follow the [session guide](/world-id/idkit/session-proofs) for the complete
Expand Down Expand Up @@ -286,7 +324,7 @@ These presets only return World ID 3.0 proofs. Use them for existing integration
<tr>
<td className="p-2 align-middle whitespace-nowrap"><code>allow_legacy_proofs</code></td>
<td className="p-2 align-middle">Request config and widgets</td>
<td className="p-2 align-middle">Required for request flows. Set to <code>true</code> while accepting World ID 3.0 fallback proofs; set to <code>false</code> for World ID 4.0-only requests.</td>
<td className="p-2 align-middle">Required for request flows. Set to <code>true</code> while accepting World ID 3.0 fallback proofs; set to <code>false</code> for World ID 4.0-only requests. Presets override it: <code>proofOfHuman</code>, <code>passport</code>, <code>mnc</code>, and <code>identityCheck</code> always accept 3.0 fallback, and <code>selfieCheck</code> never does.</td>
</tr>
<tr>
<td className="p-2 align-middle whitespace-nowrap"><code>require_user_presence</code></td>
Expand Down
9 changes: 7 additions & 2 deletions world-id/idkit/error-codes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ This page documents the IDKit SDK and bridge error codes returned during request
<td className="p-2 align-middle">Requested credential type is not available for that user.</td>
<td className="p-2 align-middle">Offer fallback credential policy or explain requirement.</td>
</tr>
<tr>
<td className="p-2 align-middle whitespace-nowrap"><code>feature_unavailable</code></td>
<td className="p-2 align-middle">Requested feature is not enabled for the app. JS/React only until the next Kotlin and Swift releases.</td>
<td className="p-2 align-middle">Get the feature enabled for your app, or fall back to a request that doesn't use it.</td>
</tr>
<tr>
<td className="p-2 align-middle whitespace-nowrap"><code>world_id_4_not_available</code></td>
<td className="p-2 align-middle">World ID 4.0 credential is not available for that user.</td>
Expand Down Expand Up @@ -145,7 +150,7 @@ This page documents the IDKit SDK and bridge error codes returned during request
</tr>
<tr>
<td className="p-2 align-middle whitespace-nowrap"><code>invalid_rp_id_format</code></td>
<td className="p-2 align-middle">RP ID is malformed.</td>
<td className="p-2 align-middle">RP ID is malformed. JS/React only — Kotlin and Swift have no equivalent code.</td>
<td className="p-2 align-middle">Use the registered <code>rp_...</code> ID from your app configuration.</td>
</tr>
<tr>
Expand All @@ -169,7 +174,7 @@ In JS and React, failed requests expose an `IDKitDebugReport` for triage. The wi

Treat version availability errors such as `world_id_4_not_available` and `world_id_3_not_available` as terminal for the current user and request. Retrying the same request usually returns the same result; change the requested credential policy or show a user-facing fallback instead.

In JS and React, match these with `IDKitErrorCodes`. Kotlin and Swift expose the same raw values through their `IDKitErrorCode` enums.
In JS and React, match these with `IDKitErrorCodes`. Kotlin and Swift expose the same raw values through their `IDKitErrorCode` enums, except `invalid_rp_id_format` (JS/React-only) and `feature_unavailable` (JS/React only until the next Kotlin and Swift releases).

```tsx
<IDKitRequestWidget
Expand Down
8 changes: 6 additions & 2 deletions world-id/idkit/integrate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ npm i @worldcoin/idkit
```

```kotlin title="Kotlin (Gradle)"
// Currently published to GitHub Packages only (not yet on Maven Central).
// Full setup (repository + credentials): /world-id/idkit/kotlin#install
dependencies {
implementation("com.worldcoin:idkit:<version>")
}
Expand Down Expand Up @@ -136,7 +138,7 @@ func handleRPSignature(w http.ResponseWriter, r *http.Request) {
</Note>

# Step 4: Generate the connect URL and collect proof
To test during development, use the [simulator](https://simulator.worldcoin.org/) and set `environment` to `"staging"`.
To test during development, use the [simulator](https://simulator.worldcoin.org/) and set `environment` to `"staging"`. To test with the sandbox build of World ID instead, see [Sandbox](/world-id/sandbox/what-is-sandbox).

<CodeGroup title="Create request and collect proof">
```typescript title="JavaScript"
Expand All @@ -162,7 +164,7 @@ const request = await IDKit.request({
signature: rpSig.sig,
},
allow_legacy_proofs: true,
environment: "production", // Only set this to staging for testing with the simulator
environment: "production", // "staging" (simulator) or "sandbox" (sandbox build of World ID)
return_to: "myapp://verify-done", // Optional: mobile deep-link callback URL
// Signal (optional): Bind specific context into the requested proof.
// Examples: user ID, wallet address. Your backend should enforce the same value.
Expand Down Expand Up @@ -298,6 +300,8 @@ val completion = request.pollUntilCompletion()
```
</CodeGroup>

> Advanced (JS/React): for custom multi-credential requests (e.g. Proof of Human AND (Passport OR MNC)), use `.constraints(...)` instead of `.preset(...)` (in React, the `constraints` prop instead of `preset`), built with the `any`/`all`/`enumerate`/`CredentialRequest` helpers from `@worldcoin/idkit-core` (also exported by `@worldcoin/idkit`). A user holds at most one [NFC credential](/world-id/credentials/9303), so passport and MNC are alternatives: combine them with `any`, never `all`. The Kotlin and Swift SDKs don't expose these helpers.

### IDKit response

After the user completes the verification flow, IDKit returns one of the following response shapes, depending on the protocol version and proof type.
Expand Down
29 changes: 25 additions & 4 deletions world-id/idkit/javascript.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ yarn add @worldcoin/idkit-core

- `IDKit.request(config)` for uniqueness proofs
- `IDKit.requestWithInviteCode(config)` for invite-code mode
- `proofOfHuman`, `passport`, `selfieCheck`, and `identityCheck` for current presets
- `IDKit.createSession(config)` and `IDKit.proveSession(sessionId, config)` for [session proofs](/world-id/idkit/session-proofs)
- `proofOfHuman`, `passport`, `mnc`, `selfieCheck`, and `identityCheck` for current presets
- `orbLegacy`, `secureDocumentLegacy`, `documentLegacy`, and `deviceLegacy` for legacy presets

Each entry point returns a builder. Finalize it with `.preset(...)`.
Each entry point returns a builder. Finalize it with `.preset(...)` for single-credential requests, or `.constraints(...)` for multi-credential and session requests — see [Constraints](#constraints).

## Request config

Expand All @@ -49,7 +50,7 @@ const builder = IDKit.request({
signature: "0x...",
},
allow_legacy_proofs: true,
environment: "production", // Only set this to staging for testing with the simulator
environment: "production", // "production" | "staging" (simulator) | "sandbox" (sandbox build of World ID, for integration testing)
return_to: "myapp://verify-done", // Optional: mobile deep-link callback URL
bridge_url: undefined, // Optional: custom bridge URL
});
Expand All @@ -72,9 +73,29 @@ const request = await IDKit.request({
}).preset(orbLegacy({ signal: "user-123" }));
```

## Constraints

`.constraints(...)` builds custom World ID 4.0 credential requests, including composite trees (e.g. Proof of Human AND (Passport OR MNC)), using `CredentialRequest`, `any`, `all`, and `enumerate`. It's the only finalizer `IDKit.createSession()`/`IDKit.proveSession()` builders accept — `.preset()` throws on a session builder.

```ts
import { IDKit, CredentialRequest, any, all } from "@worldcoin/idkit-core";

const request = await IDKit.request({
app_id: "app_xxxxx",
action: "my-action",
rp_context,
allow_legacy_proofs: false,
}).constraints(
all(
CredentialRequest("proof_of_human"),
any(CredentialRequest("passport"), CredentialRequest("mnc")),
),
);
```

## Polling and status

After `.preset(...)`, you get an `IDKitRequest` object:
After `.preset(...)` or `.constraints(...)`, you get an `IDKitRequest` object:

- `connectorURI`
- `requestId`
Expand Down
2 changes: 1 addition & 1 deletion world-id/idkit/mini-apps.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The Mini-App-specific details:
| Passport-backed checks | `passport` |

For repeated Selfie Check verification of returning users, use
[session proofs](/world-id/idkit/session-proofs) with the `selfieCheck()` preset.
[session proofs](/world-id/idkit/session-proofs) with `.constraints(CredentialRequest("selfie"))`.

Check out this [page](/world-id/idkit/credentials) to learn about the different World ID credentials and which preset to use for each.

Expand Down
8 changes: 7 additions & 1 deletion world-id/idkit/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,19 @@ Hook result fields:

- `open()`
- `reset()`
- `isOpen`
- `isAwaitingUserConnection`
- `isAwaitingUserConfirmation`
- `isSuccess`
- `isError`
- `isInWorldApp` — true when running inside the World App (mini app context)
- `connectorURI`
- `result`
- `errorCode`
- `getDebugReport()`

`useIDKitRequest` and `useIDKitSession` return `IDKitHookResult<T>`. `useIDKitInviteCodeRequest` returns `IDKitInviteCodeHookResult<T>`, which includes these common fields plus `codeExpiresAt`, the invite code's expiration timestamp in Unix seconds (or `null` before a code is available).

## Invite-code mode

For invite-code flows, use `IDKitInviteCodeRequestWidget` (controlled) or `useIDKitInviteCodeRequest` (headless). Config matches `IDKitRequestWidget` / `useIDKitRequest` — invite-code mode adds no new required fields. See [Invite-code mode](/world-id/idkit/verification-flows#with-invite-code-mode) for when to use it.
Expand All @@ -141,10 +145,12 @@ import { IDKitInviteCodeRequestWidget, proofOfHuman } from "@worldcoin/idkit";

- `open()`
- `reset()`
- `isOpen`
- `isAwaitingUserConnection`
- `isAwaitingUserConfirmation`
- `isSuccess`
- `isError`
- `isInWorldApp`
- `connectorURI`
- `codeExpiresAt`
- `result`
Expand Down Expand Up @@ -195,7 +201,7 @@ Session verification uses `IDKitSessionWidget` (controlled) or `useIDKitSession`

## Presets

React hooks/widgets take `preset` directly in config.
React hooks/widgets take `preset` or `constraints` in config — never both — for single- or multi-credential requests respectively. Session hooks/widgets (`useIDKitSession`, `IDKitSessionWidget`) require `constraints`; they have no `preset` option.

## Localization and UX notes

Expand Down
Loading
Loading