Petri

World ID · Continuity Track

Left, sections 1–5: Best IDKit Use Case. Right, sections 6–11: Best Use of World ID for Agents. Each section shows its own status.

Best IDKit Use Case · sections 1–5

Withdraw only if you are the same human who opened the account

Before a withdrawal runs, the app asks World ID for a Selfie Check. The server verifies the proof with the Developer Portal and checks that it comes from the same human who opened the account, within the last hour.

  1. 1IDKit integration
  2. 2World ID credential
  3. 3Server verification
  4. 4Alternative path
  5. 5Integration debrief
1

IDKit integration

Configured, preflight not run

@worldcoin/idkit renders the World ID request. The server signs each request with the RP key (/api/world/selfie-check/context), so the key never reaches the browser.

App ID
app_63ca83f0764b41c39e7a66c9e16d8bdc
RP ID
rp_d75ba8286d32d7f0
Action
continuity-gate
Environment
production
Protocol
3.0
2

World ID credential: Selfie Check

Not tested yet

Trust event: a withdrawal can't be undone. Before it runs, the product needs to know that the person asking is the same human who opened the account, not someone who stole the session or the device.

CredentialWhat it provesChoice
Proof of Human (Orb)Unique human, globalMore than needed. Most users would have to visit an Orb.
Passport / NFCDocument attributes (age, nationality)Not relevant here, and collects more than this event needs.
Selfie CheckA live face on a phone, with the same nullifier every time for the same personChosen, the minimum that is enough. A matching nullifier means it is the same human, and a 1h max_age means the check is recent.

This opens the real IDKit widget. Scan the QR code with World App. Closing the sheet counts as a cancellation in section 4.

3

Verify the result on the server

Not tested yet

The widget's result is only forwarded to /api/world/selfie-check/verify. The server checks the protocol version, the credential, the account binding (signal_hash) and the nonce, then calls POST developer.world.org/api/v4/verify/{rp_id}. The withdrawal runs only if the server allows it.

Protected action · Withdraw funds: Locked

No proof has been verified yet. Run section 2 first.

4

Alternative path: the action does not run

Not tested yet

Every unsuccessful path is logged here, and none of them unlock the withdrawal. Cancel the World App sheet, get a World App error (credential_unavailable, user_rejected), or send these to the real verify route:

No unsuccessful attempts yet.

5

Integration debrief

Written
Time to first success
  • First integration: about 2.5 days. We started on 2026-09-10, and the portal verified the first Selfie Check proof on 2026-09-12 at 14:21 UTC (HTTP 200). Most of that time went to the protocol version and verify endpoint problems listed below.
  • A new app with the same code: minutes. On 2026-09-26 we created a new app, pasted its App ID, RP ID and signing key, and the first proof verified at 05:49 UTC.
Friction
  • Selfie Check can only be issued on World ID 3.0. The 4.0 request type-checks but returns credential_unavailable.
  • allow_legacy_proofs is required and not documented, and the SDK examples disagree on its value.
  • The v2 verify endpoint returns invalid_action for every action once RP registration is active. Only v4 works.
  • IDKit accepts sandbox, but the verify endpoint has no sandbox environment.
Missing capability / docs
  • No page says that signal_hash equals hash_to_field(signal).
  • sybil_score is required by v4 for 4.0 Selfie Check but missing from the SDK types.
  • feature_unavailable and all_verifications_failed are missing from the error reference.
  • The sandbox has no test users.
The one improvement with the biggest impact
State on the Selfie Check credential page which protocol version it can be issued on. That one sentence would have saved most of our time.

The full write-up is in FEEDBACK.md.

Best Use of World ID for Agents · sections 6–11

An AI agent can't spend your money until you approve it with World ID

Before running a payment, the agent asks for approval. You approve on World's approval page with a fresh proof (mocked in the event sandbox). The backend validates the ID token and checks that the approver is the agent's owner. Only then does the action run.

  1. 6Agent integration
  2. 7Approval request
  3. 8Backend validation
  4. 9Protected action
  5. 10Unsuccessful path
  6. 11Integration debrief
6

World ID for Agents integration

Blocked

The agent's backend is a confidential OIDC client of the sandbox World ID IdP. It uses the device authorization grant: the agent never needs a browser callback, and every approval requires a fresh World ID proof.

Not configured. Add to .env.local:
  • WORLD_AGENT_CLIENT_ID: Not set.
  • WORLD_AGENT_CLIENT_SECRET: Not set.
7

Agent asks the human for approval

Not tested yet
Agent task
Send 25 USDC to merchant.eth
The agent found an invoice and wants to pay it on your behalf. The transfer is simulated; no funds move.
8

Validate the result on the backend

Not tested yet

When World approves, the backend redeems the device code for an ID token and validates it itself: the RS256 signature against World's JWKS, issuer, audience, expiry, and auth_time inside this attempt. It also checks that the approver is the agent's owner. The browser never gets the token.

Nothing to validate yet. Approve a request in section 7.

9

Protected agent action

Locked
Send 25 USDC to merchant.eth: Locked. It runs only after section 8 passes.
10

Unsuccessful path: the action does not run

Not tested yet

To test these: click Deny on World's approval page, choose the 30 s window and let it expire, click Cancel request, or send a forged token to the real validator. To test a different human, reset the owner and approve from another World ID.

No unsuccessful attempts yet.

11

Integration debrief

Written
Time to first success
One working session on 2026-09-26: reading the guides, registering the client, writing the device flow and JWKS validation. The first approved and validated request ran the agent action at 7:28 PM local time. The only failed attempt before it was approving an old, already-cancelled code by mistake. The backend correctly ignored that one.
Friction
  • The public /docs page lists no endpoints or fields. The real guides (oidc, step-up) are only in the MCP server, so reading them takes a JSON-RPC client.
  • Sandbox rejects http://localhost callbacks, and a device-only client still has to register an HTTPS redirect URI it never uses.
  • The device grant ignores max_age, prompt and acr_values. Freshness is implied, so the backend has to check auth_time against the attempt start itself.
Missing capability / docs
  • We found no official JS helper for the device grant plus ID-token validation, so we wrote the JWKS check ourselves.
  • No binding message. The approval page shows only the user code, not what the agent wants to do. With two requests open, it's easy to approve the wrong one, which happened to us.
The one improvement with the biggest impact
Let the agent attach a short, human-readable action description to the device request (like CIBA's binding_message) and show it on the approval screen, so people approve a specific action, not just a login.