diff --git a/images/docs/world-id/idkit/invite-code-demo.mp4 b/images/docs/world-id/idkit/invite-code-demo.mp4 new file mode 100644 index 0000000..0eadf35 Binary files /dev/null and b/images/docs/world-id/idkit/invite-code-demo.mp4 differ diff --git a/world-id/idkit/javascript.mdx b/world-id/idkit/javascript.mdx index 2e7d823..d45e7fa 100644 --- a/world-id/idkit/javascript.mdx +++ b/world-id/idkit/javascript.mdx @@ -105,7 +105,7 @@ if (!completion.success) { ## Invite-code mode -Use `IDKit.requestWithInviteCode(config)` to display a short code instead of a QR. Validation, the returned `Status` shape, and the poll loop are identical to `IDKit.request`. See [Invite-code mode](/world-id/idkit/verification-flows#with-invite-code-mode) for when to use it. +Use `IDKit.requestWithInviteCode(config)` to open a landing page that displays both an invite code and a QR code. Validation, the returned `Status` shape, and the poll loop are identical to `IDKit.request`. See [Invite-code mode](/world-id/idkit/verification-flows#with-invite-code-mode) for when to use it. ```ts import { IDKit, selfieCheckLegacy } from "@worldcoin/idkit-core"; @@ -117,14 +117,14 @@ const request = await IDKit.requestWithInviteCode({ allow_legacy_proofs: true, }).preset(selfieCheckLegacy({ signal: "user-123" })); -const code = request.code; // 6-character canonical form -const expiresAt = request.expiresAt; // Unix seconds +const connectorURI = request.connectorURI; // URL with &c=&a= +const expiresAt = request.expiresAt; // Unix seconds const completion = await request.pollUntilCompletion(); ``` `IDKitInviteCodeRequest` exposes: -- `code` +- `connectorURI` - `expiresAt` - `requestId` - `pollOnce()` @@ -154,12 +154,12 @@ const request = await IDKit.requestWithInviteCode({ allow_legacy_proofs: true, }).preset(selfieCheckLegacy({ signal: "user-123" })); -const code = request.code; // display to user +const connectorURI = request.connectorURI; // display to user (URL with code embedded) const expiresAt = request.expiresAt; // drive a countdown const completion = await request.pollUntilCompletion(); ``` -The config object is unchanged. Replace `connectorURI` (QR) with `code` and `expiresAt` in your UI. Polling, proof verification, and nullifier storage stay the same. +The config object is unchanged. The `connectorURI` now includes `&c=&a=` params; use it alongside `expiresAt` in your UI. Polling, proof verification, and nullifier storage stay the same. ## Server-side helpers diff --git a/world-id/idkit/react.mdx b/world-id/idkit/react.mdx index 2f96234..c551255 100644 --- a/world-id/idkit/react.mdx +++ b/world-id/idkit/react.mdx @@ -144,7 +144,7 @@ import { IDKitInviteCodeRequestWidget, selfieCheckLegacy } from "@worldcoin/idki - `isAwaitingUserConfirmation` - `isSuccess` - `isError` -- `code` +- `connectorURI` - `codeExpiresAt` - `result` - `errorCode` diff --git a/world-id/idkit/swift.mdx b/world-id/idkit/swift.mdx index 5c8ed56..95585b5 100644 --- a/world-id/idkit/swift.mdx +++ b/world-id/idkit/swift.mdx @@ -84,8 +84,8 @@ Use `presetWithInviteCode(_:)` on the builder to return an `IDKitInviteCodeReque let request = try IDKit.request(config: config) .presetWithInviteCode(selfieCheckLegacy(signal: "user-123")) -let code = request.code // 6-character canonical form -let expiresAt = request.expiresAt // Date +let connectorURL = request.connectorURL // URL with &c=&a= +let expiresAt = request.expiresAt // Date let completion = await request.pollUntilCompletion() ``` @@ -105,17 +105,17 @@ let completion = await request.pollUntilCompletion() let request = try IDKit.request(config: config) .presetWithInviteCode(selfieCheckLegacy(signal: "user-123")) -let code = request.code // display to user +let connectorURL = request.connectorURL // display to user (URL with code embedded) let expiresAt = request.expiresAt // drive a countdown let completion = await request.pollUntilCompletion() ``` -The config object is unchanged. Replace `connectorURL` (QR) with `code` and `expiresAt` in your UI. Polling, proof verification, and nullifier storage stay the same. +The config object is unchanged. The `connectorURL` now includes `&c=&a=` params; use it alongside `expiresAt` in your UI. Polling, proof verification, and nullifier storage stay the same. `IDKitInviteCodeRequest` exposes: -- `code: String` +- `connectorURL: URL` - `expiresAt: Date` -- `requestID: UUID` +- `requestID: String` - `pollStatusOnce() async -> IDKitStatus` - `pollUntilCompletion(options:) async -> IDKitCompletionResult` diff --git a/world-id/idkit/verification-flows.mdx b/world-id/idkit/verification-flows.mdx index 0db3e4c..ffc7364 100644 --- a/world-id/idkit/verification-flows.mdx +++ b/world-id/idkit/verification-flows.mdx @@ -19,7 +19,7 @@ The cold flow requires the most steps and works differently on each platform bec ### Android -Android supports deferred deep linking through the Play Store. After the user downloads World App, the original verification context is carried forward at first launch — World App picks up where the user left off and routes them directly into credential enrollment. No re-trigger from your app is needed. +Android supports deferred deep linking through the Play Store. After the user downloads World App, the original verification context is carried forward at first launch — World App picks up where the user left off and routes them directly into credential enrollment. ```mermaid sequenceDiagram @@ -49,8 +49,6 @@ iOS does not support deferred deep linking through the App Store. The original v 3. World App opens and takes the user through a hot or warm flow. 4. The proof consent appears, the user approves, and the proof is returned to your app. -Because iOS does not preserve context through the App Store install, your app should direct the user to install World App before issuing the IDKit request. - ```mermaid sequenceDiagram participant App as Your App @@ -80,15 +78,25 @@ Invite-code mode displays a short 6-character code in your app that the user ent 1. Your app triggers an IDKit invite-code request. -2. Your app opens the URL that IDKit provides. A landing page displays the invite code and a QR code for the request. -3. The user downloads World App from the App Store. -4. The user opens World App and completes account creation onboarding. -5. The user enters the invite code. -6. World App resolves the code, picks up the original verification context, and walks the user through credential enrollment. -7. The proof consent appears, the user approves, and the proof is returned to your app. +2. Your app opens the URL that IDKit provides. One of three paths follows: + - **User has World App (mobile):** World App launches directly via deep link. + - **User has World App (desktop):** The user scans the QR code with World App. + - **User needs to install World App:** The user installs World App, completes account onboarding, then enters the invite code to resume the request. +3. World App restores the verification context, walks the user through credential enrollment if needed, and presents a proof consent. +4. The user approves and the proof is returned to your app. The 6-character code persists across the App Store install, so once World App is installed and onboarded the user can resume the verification flow without returning to your app first. This also covers cross-device scenarios (e.g., a desktop browser displaying the QR for the user's phone) where deep linking cannot carry context. +##### Demo + +
+ +
+ +##### Flow diagram + ```mermaid sequenceDiagram participant App as Your App @@ -109,9 +117,11 @@ sequenceDiagram WA-->>App: Proof returned ``` +
+ ##### Lifecycle -- Codes expire after a short TTL (currently ten minutes). +- Codes expire after a short TTL (currently fifteen minutes). - Codes are one-shot — once redeemed, they cannot be reused. Re-running the request returns a fresh code with a fresh TTL. - After the user redeems the code, your existing poll loop receives the proof exactly as it does in QR mode. @@ -139,8 +149,8 @@ const request = await IDKit.requestWithInviteCode({ environment: "production", }).preset(selfieCheckLegacy({ signal: "user-123" })); -const code = request.code; // 6-character canonical form -const expiresAt = request.expiresAt; // Unix seconds +// Open the landing page — displays invite code + QR +window.open(request.connectorURI, "_blank"); const completion = await request.pollUntilCompletion(); ``` @@ -205,8 +215,8 @@ let config = IDKitRequestConfig( let request = try IDKit.request(config: config) .presetWithInviteCode(selfieCheckLegacy(signal: "user-123")) -let code = request.code // 6-character canonical form -let expiresAt = request.expiresAt // Date +// Open the landing page — displays invite code + QR +await UIApplication.shared.open(request.connectorURL) let completion = await request.pollUntilCompletion() ```