Skip to main content
This guide helps you migrate from @worldcoin/idkit-standalone to @worldcoin/idkit-core. The idkit-standalone package has been discontinued. If you used it for a vanilla JavaScript or custom QR flow, migrate to @worldcoin/idkit-core. ¡

Migration Checklist

  1. Enable World ID 4.0 in the Developer Portal and keep your app_id, rp_id, and server-only signing_key.
  2. Install @worldcoin/idkit-core.
  3. Add a backend endpoint that generates an RP signature. See RP Signatures.
  4. Pick a legacy preset that matches your previous verification_level configuration.
  5. Replace verifyCloudProof(...) with a backend POST to https://developer.world.org/api/v4/verify/{rp_id}.
  6. Store the verified nullifier for replay protection.
For the broader protocol migration, including proof types and timelines, see World ID 4.0.

Install

npm i @worldcoin/idkit-core

Request a Proof

In standalone, the package mounted UI through browser globals:
Before
import "@worldcoin/idkit-standalone";

IDKit.init({
  app_id: "app_xxxxx",
  action: "my-action",
  signal: "user-123",
  verification_level: "orb",
});

await IDKit.open();
With idkit-core, fetch an RP signature from your backend, build the request, render the connectorURI, and poll until World App returns a proof.
After
import { IDKit, orbLegacy } from "@worldcoin/idkit-core";

const action = "my-action";
const signal = "user-123";

const rpContext = await fetch("/api/rp-signature", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ action }),
}).then((r) => r.json());

const request = await IDKit.request({
  app_id: "app_xxxxx",
  action,
  rp_context: {
    rp_id: "rp_xxxxx",
    nonce: rpContext.nonce,
    created_at: rpContext.created_at,
    expires_at: rpContext.expires_at,
    signature: rpContext.sig,
  },
  allow_legacy_proofs: true,
  environment: "production",
}).preset(orbLegacy({ signal }));

renderQrCode(request.connectorURI);

const completion = await request.pollUntilCompletion({
  pollInterval: 2_000,
  timeout: 120_000,
});

if (!completion.success) {
  throw new Error(`World ID verification failed: ${completion.error}`);
}

await fetch("/api/verify-proof", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ idkitResponse: completion.result }),
});

Credential Mapping

Standalone used verification_level to choose what the user should prove. In idkit-core, use a legacy preset instead. These presets are drop-in replacements for the old verification levels.
The legacy presets will return the maximum credential a user has. For example, if a user has an Orb credential, but you request documentLegacy, they will verify with their Orb credential.
Standaloneidkit-core preset
verification_level: "orb"orbLegacy({ signal })
verification_level: "secure_document"secureDocumentLegacy({ signal })
verification_level: "document"documentLegacy({ signal })
verification_level: "device"deviceLegacy({ signal })
import {
  IDKit,
  deviceLegacy,
  documentLegacy,
  orbLegacy,
  secureDocumentLegacy,
} from "@worldcoin/idkit-core";

const request = await IDKit.request(config).preset(
  orbLegacy({ signal: "user-123" }),
);

RP Signatures

IDKit 4.x requests require rp_context, signed by your backend with the Developer Portal signing_key. Do not generate signatures in client code. For implementation details, use the RP Signatures reference. It includes the JavaScript helper, the signing algorithm, and test vectors for non-JavaScript backends.

Verify the Proof

Standalone integrations usually verified with verifyCloudProof(...):
Before
import { verifyCloudProof } from "@worldcoin/idkit";

const response = await verifyCloudProof(proof, app_id, action, signal);
In IDKit 4.x, send the IDKit result to your backend and forward it directly to the v4 verify endpoint.
After
import type { IDKitResult } from "@worldcoin/idkit-core";

export async function POST(request: Request): Promise<Response> {
  const { idkitResponse } = (await request.json()) as {
    idkitResponse: IDKitResult;
  };

  const response = await fetch(
    `https://developer.world.org/api/v4/verify/${process.env.WORLD_ID_RP_ID}`,
    {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(idkitResponse),
    },
  );

  return new Response(await response.text(), {
    status: response.status,
    headers: { "content-type": "application/json" },
  });
}

Store the Nullifier

After /api/v4/verify/{rp_id} succeeds, store the verified nullifier for the action and reject duplicates. During migration, keep checking any old nullifier_hash records if the same user could have verified before the upgrade.