Skip to main content
@worldcoin/idkit-core is the lowest-level JavaScript/TypeScript IDKit SDK. Use it when you want full control over UI and state management or when you’re not using React.

Install

npm i @worldcoin/idkit-core

Entry points

  • IDKit.request(config) for uniqueness proofs
  • IDKit.createSession(config) for first-time session creation
  • IDKit.proveSession(sessionId, config) for returning users
  • CredentialRequest, any, all for explicit constraints
  • orbLegacy, secureDocumentLegacy, documentLegacy for presets
Each entry point returns a builder. Finalize it with either .constraints(...) or .preset(...).

Request config

import { IDKit } from "@worldcoin/idkit-core";

const builder = IDKit.request({
  app_id: "app_xxxxx",
  action: "my-action",
  rp_context: {
    rp_id: "rp_xxxxx",
    nonce: "0x...",
    created_at: 1735689600,
    expires_at: 1735689900,
    signature: "0x...",
  },
  action_description: "Verify user",
  bridge_url: undefined,
  allow_legacy_proofs: false,
  override_connect_base_url: undefined,
  environment: "production",
});
Generate rp_context in your backend only. Never expose your RP signing key in client code.

Constraints

Use constraints when you want explicit credential logic.
import { IDKit, CredentialRequest, any, all } from "@worldcoin/idkit-core";

const orb = CredentialRequest("orb", { signal: "user-123" });
const face = CredentialRequest("face", { signal: "user-123" });

const secureDocWithMinGenesis = CredentialRequest("secure_document", {
  signal: "user-123",
  genesis_issued_at_min: 1_735_689_600,
});

const request = await IDKit.request({
  app_id: "app_xxxxx",
  action: "my-action",
  rp_context,
  allow_legacy_proofs: false,
}).constraints(all(any(orb, face), secureDocWithMinGenesis));
CredentialRequest supports:
  • signal: Extra context bound into the proof (for example wallet_address or user_id). The value is hashed and committed in the proof.
  • genesis_issued_at_min: Only accept credentials with credential.genesis_issued_at >= genesis_issued_at_min.
  • expires_at_min: Only accept credentials with credential.expires_at > expires_at_min. If you omit it, IDKit uses the request created_at timestamp.

Presets

import { IDKit, orbLegacy } from "@worldcoin/idkit-core";

const request = await IDKit.request({
  app_id: "app_xxxxx",
  action: "my-action",
  rp_context,
  allow_legacy_proofs: true,
}).preset(orbLegacy({ signal: "user-123" }));
Use presets only when migrating from an older IDKit version. Preset-based requests are backward compatible and can work with both World ID 4.0 and World ID 3.0 (legacy) flows.

Sessions

Sessions are v4-only and return session_id.
import { IDKit, CredentialRequest, any, orbLegacy } from "@worldcoin/idkit-core";

const sessionConfig = {
  app_id: "app_xxxxx",
  rp_context,
  action_description: "Sign in",
  environment: "production" as const,
};

const createRequest = await IDKit.createSession(sessionConfig).constraints(
  any(CredentialRequest("orb")),
);

const createResult = await createRequest.pollUntilCompletion();
if (!createResult.success) throw new Error(createResult.error);

const sessionId = createResult.result.session_id;

const proveRequest = await IDKit.proveSession(sessionId, sessionConfig).preset(
  orbLegacy({ signal: "user-123" }),
);
const proveResult = await proveRequest.pollUntilCompletion();
In session flows, there is no allow_legacy_proofs toggle. Sessions are always World ID 4.0.

Polling and status

After .constraints(...) or .preset(...), you get an IDKitRequest object:
  • connectorURI
  • requestId
  • pollOnce()
  • pollUntilCompletion({ pollInterval, timeout, signal })
import { IDKitErrorCodes } from "@worldcoin/idkit-core";

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

if (!completion.success) {
  if (completion.error === IDKitErrorCodes.Timeout) {
    // UI timeout handling
  }
  if (completion.error === IDKitErrorCodes.Cancelled) {
    // User/app cancellation handling
  }
}
When running inside World App, native transport is used and connectorURI may be empty. Outside World App, connectorURI is the URL you render as a QR code.

Server-side helpers

Use subpath exports on your backend:
import { signRequest } from "@worldcoin/idkit-core/signing";
import { hashSignal } from "@worldcoin/idkit-core/hashing";

const { sig, nonce, createdAt, expiresAt } = signRequest(
  "my-action",
  process.env.RP_SIGNING_KEY!,
);

const signalHash = hashSignal("user-123");
signRequest should only run in trusted server environments.