Skip to main content
Use IDKit sessions when you want to link user actions over time. Unlike IDKit.request flows used for one-time actions, sessions return a session_id that you can save and reuse in later interactions.

Step 1: Generate an RP signature (backend only)

Sessions still require signed rp_context, generated server-side.
import { signRequest } from "@worldcoin/idkit-core/signing";

export async function POST(): Promise<Response> {
  const signingKey = process.env.RP_SIGNING_KEY;

  if (!signingKey) {
    return Response.json({ error: "Missing RP_SIGNING_KEY" }, { status: 500 });
  }

  const { sig, nonce, createdAt, expiresAt } = signRequest(
    "session-auth",
    signingKey,
  );

  return Response.json({
    sig,
    nonce,
    created_at: createdAt,
    expires_at: expiresAt,
  });
}
Never generate RP signatures on the client and never expose your RP signing key. If the key leaks, attackers can forge requests as your app.

Step 2: Create or prove the session in your client

  • createSession: first-time user, no saved session_id.
  • proveSession: returning user with existing session_id.
import { IDKit, CredentialRequest } from "@worldcoin/idkit-core";

const rpSig = await fetch("/api/rp-signature", {
  method: "POST",
}).then((r) => r.json());

const sessionConfig = {
  app_id: "app_xxxxx",
  rp_context: {
    rp_id: "rp_xxxxx",
    nonce: rpSig.nonce,
    created_at: rpSig.created_at,
    expires_at: rpSig.expires_at,
    signature: rpSig.sig,
  },
  environment: "production",
};

const orbCredential = CredentialRequest("orb");
const builder = existingSessionId
  ? IDKit.proveSession(existingSessionId, sessionConfig)
  : IDKit.createSession(sessionConfig);

const request = await builder.constraints(orbCredential);
const connectUrl = request.connectorURI;
const completion = await request.pollUntilCompletion();

if (completion.success) {
  const sessionId = completion.result.session_id;
  const sessionNullifier = completion.result.responses[0]?.session_nullifier;
  // Persist sessionId for future proveSession() calls.
}

Step 3: Verify the proof in your backend and persist session_id

After successful completion, send the returned payload to your backend and forward it directly to: POST https://developer.world.org/api/v4/verify/{rp_id}.
app.post("/api/verify-session", async (req, res) => {
  const { rp_id, devPortalPayload } = 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(devPortalPayload),
    },
  );

  const payload = await response.json();

  if (response.ok && devPortalPayload.session_id) {
    await saveUserSessionId(req.user.id, devPortalPayload.session_id);
  }

  res.status(response.ok ? 200 : response.status).json(payload);
});
On first success, save session_id to your user record. On later flows, load it and call proveSession.