From fcb0bdfdd910024880e23058dec420f88157b209 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Sun, 20 Sep 2026 20:58:34 -0700 Subject: [PATCH 1/8] docs(mini-apps): fix 44 audit findings across commands, quick-start, guidelines, growth, and reference - mini-apps/reference/status-page.mdx: point Get Status API examples at the live status.world.org host instead of the dead status.worldcoin.org - mini-apps/sharing/sage-qa.mdx: flag the page as deprecated since the Sage Developer dashboard deployment is disabled (HTTP 402) - mini-apps/guidelines/app-guidelines.mdx: remove leftover raw JSX-attribute fragments dangling after the Package README link - mini-apps/quick-start/init.mdx: fix getPermissions() sample to compile under strict TS; document that window.WorldApp needs a hand-written ambient declaration - mini-apps/commands/wallet-auth.mdx: await the async cookies() API (Next.js 15+); add missing optional version field to the Result type; correct fallback signature to allow sync/async returns - mini-apps/commands/pay.mdx: drop the incompatible explicit generic on MiniKit.pay() result; document the optional network field; add a Supported Tokens table; correct fallback signature - mini-apps/commands/chat.mdx: correct fallback signature to allow sync/async returns - mini-apps/reference/credit-api.mdx: document that the endpoint requires authorization and add 401 to the Error codes table - mini-apps/sharing/dna-qa.mdx: add the required tab param to Example Usage and regenerate the Generated Deeplink URL to match - mini-apps/reference/address-book.mdx: correct docs to state getIsUserVerified resolves false (rather than throwing) on internal failure - mini-apps/migration/minikit-v2.mdx: remove the inaccurate "signTypedData deprecated" breaking-change bullet - mini-apps/growth/notifications.mdx: reconcile the badge open-rate threshold to 15% everywhere; remove the unverifiable 7-day notification pause claim; document the 200-char message length cap - mini-apps/growth/invites-viral.mdx: fix the undefined YOUR_APP_ID reference and URL-encode the path in the Step 1 sample; fence it as tsx with a proper React import; add self-referral/idempotency/verification checks to the reward sample and correct the Next Steps claim; remove the unverified open_out_of_window param; add a note mapping its event names to analytics.mdx's canonical set; clarify trackEvent is a placeholder; recommend MiniKit.getMiniAppUrl for building deep links - mini-apps/quick-start/installing.mdx: fix the pnpm/npm recommendation mismatch, the broken Codex MCP command, and the Claude Code/Codex/Cursor/VS Code MCP server-name and scope inconsistency with model-context-protocol/world-docs.mdx; document the useMiniKit() hook - mini-apps/guidelines/features-and-guidelines.mdx: escape the apostrophe in the notification cURL example so it is shell-safe - mini-apps/commands/send-transaction.mdx: document that poll() throws on failure and has no built-in timeout, and expand the API type/example to cover pending/failed statuses and 400/500 errors - mini-apps/reference/usernames.mdx: document getUserByUsername/getUserInfo, their return shape, and that unknown users resolve with undefined fields rather than throwing - mini-apps/quick-start/testing.mdx: standardize on the world.org domain to match commands.mdx - mini-apps/quick-start/responses.mdx: fill in the empty Event Subscriptions section with real subscribe/unsubscribe/trigger guidance - mini-apps/commands/sign-message.mdx: add a TypeScript setup note about the required skipLibCheck workaround - mini-apps/sharing/swap-qa.mdx: regenerate the Example link to match the Example usage code's actual output - mini-apps/commands/request-permission.mdx: add the real world_app_permission_not_enabled/mini_app_permission_not_enabled error codes - mini-apps/migration/standalone-dapp.mdx: fix the dead IDKit link to the current /world-id/idkit/mini-apps path - mini-apps/migration/web-to-miniapp.mdx, mini-apps/migration/standalone-dapp.mdx: fix the npx skills add command to use -s so it installs only the intended skill - mini-apps/more/community-tools-perks.mdx: update the stale Developer Rewards Pilot card to match the current, ongoing Builder Rewards program - mini-apps/commands/share-contacts.mdx: note getContacts as a documented alias of shareContacts --- mini-apps/commands/chat.mdx | 2 +- mini-apps/commands/pay.mdx | 24 +++++++-- mini-apps/commands/request-permission.mdx | 2 + mini-apps/commands/send-transaction.mdx | 51 ++++++++++++++----- mini-apps/commands/share-contacts.mdx | 2 +- mini-apps/commands/sign-message.mdx | 4 ++ mini-apps/commands/wallet-auth.mdx | 10 ++-- mini-apps/growth/invites-viral.mdx | 38 +++++++++++--- mini-apps/growth/notifications.mdx | 9 ++-- mini-apps/guidelines/app-guidelines.mdx | 3 -- .../guidelines/features-and-guidelines.mdx | 2 +- mini-apps/migration/minikit-v2.mdx | 1 - mini-apps/migration/standalone-dapp.mdx | 4 +- mini-apps/migration/web-to-miniapp.mdx | 2 +- mini-apps/more/community-tools-perks.mdx | 6 +-- mini-apps/quick-start/init.mdx | 21 +++++++- mini-apps/quick-start/installing.mdx | 25 +++++++-- mini-apps/quick-start/responses.mdx | 17 ++++++- mini-apps/quick-start/testing.mdx | 4 +- mini-apps/reference/address-book.mdx | 3 +- mini-apps/reference/credit-api.mdx | 5 ++ mini-apps/reference/status-page.mdx | 6 +-- mini-apps/reference/usernames.mdx | 14 +++++ mini-apps/sharing/add-money-qa.mdx | 14 ++--- mini-apps/sharing/dna-qa.mdx | 3 +- mini-apps/sharing/sage-qa.mdx | 4 ++ mini-apps/sharing/swap-qa.mdx | 2 +- 27 files changed, 209 insertions(+), 69 deletions(-) diff --git a/mini-apps/commands/chat.mdx b/mini-apps/commands/chat.mdx index 754de44..6907aac 100644 --- a/mini-apps/commands/chat.mdx +++ b/mini-apps/commands/chat.mdx @@ -38,7 +38,7 @@ export async function shareToChat() { type MiniKitChatOptions = { message: string; to?: string[]; - fallback?: () => unknown; + fallback?: () => Promise | MiniAppChatSuccessPayload; }; ``` diff --git a/mini-apps/commands/pay.mdx b/mini-apps/commands/pay.mdx index 4815917..81b0533 100644 --- a/mini-apps/commands/pay.mdx +++ b/mini-apps/commands/pay.mdx @@ -7,6 +7,22 @@ description: "Request a payment from the user" This command is an abstraction for a simple transfer. This shouldn't be used outside of World App. Pay supports WLD and all local stablecoins. +## Supported Tokens + +Import the `Tokens` enum rather than hardcoding symbol strings — note that some enum values differ from their display name (e.g. USDC's value is `"USDCE"`): + +| `Tokens` member | Enum value | Currency | +| --- | --- | --- | +| `Tokens.WLD` | `"WLD"` | Worldcoin | +| `Tokens.USDC` | `"USDCE"` | USD Coin | +| `Tokens.EURC` | `"EURC"` | Euro Coin | +| `Tokens.WARS` | `"WARS"` | Argentine Peso stablecoin | +| `Tokens.WCOP` | `"WCOP"` | Colombian Peso stablecoin | +| `Tokens.WMXN` | `"WMXN"` | Mexican Peso stablecoin | +| `Tokens.WBRL` | `"WBRL"` | Brazilian Real stablecoin | +| `Tokens.WPEN` | `"WPEN"` | Peruvian Sol stablecoin | +| `Tokens.WCLP` | `"WCLP"` | Chilean Peso stablecoin | + ## Basic Usage @@ -15,9 +31,7 @@ import { MiniKit } from "@worldcoin/minikit-js"; import { Tokens, tokenToDecimals, - type CommandResultByVia, type MiniKitPayOptions, - type PayResult, } from "@worldcoin/minikit-js/commands"; export async function sendPayment() { @@ -40,8 +54,7 @@ export async function sendPayment() { }, }; - const result: CommandResultByVia = - await MiniKit.pay(input); + const result = await MiniKit.pay(input); await fetch("/api/confirm-payment", { method: "POST", @@ -62,7 +75,8 @@ type MiniKitPayOptions = { token_amount: string; }[]; description: string; - fallback?: () => unknown; + network?: Network; // Optional; currently only "worldchain" and defaults to World Chain + fallback?: () => Promise | PayResult; }; ``` diff --git a/mini-apps/commands/request-permission.mdx b/mini-apps/commands/request-permission.mdx index 5ec66c4..160cd19 100644 --- a/mini-apps/commands/request-permission.mdx +++ b/mini-apps/commands/request-permission.mdx @@ -89,6 +89,8 @@ Define a custom fallback in the command payload for support outside mini apps. | `permission_disabled` | The permission is disabled | | `already_granted` | The permission is already granted | | `unsupported_permission` | The permission is not supported | +| `world_app_permission_not_enabled` | The permission is disabled at the World App level; prompt the user to enable it in World App settings | +| `mini_app_permission_not_enabled` | The permission is disabled at the mini app level; prompt the user to enable it in World App settings | ## Preview diff --git a/mini-apps/commands/send-transaction.mdx b/mini-apps/commands/send-transaction.mdx index dee6377..a6abc55 100644 --- a/mini-apps/commands/send-transaction.mdx +++ b/mini-apps/commands/send-transaction.mdx @@ -173,6 +173,10 @@ submitted, so the first identifier you receive is `userOpHash`. You can either use the `@worldcoin/minikit-react` hook or poll the Developer Portal to check when the user operation is mined and get the final `transaction_hash`. + +`poll()` runs an unbounded loop with no built-in timeout, and it **throws** (`Error("Transaction failed")`) if the user operation's status resolves to `failed`. Always call it inside a `try`/`catch`, and wrap it in your own timeout/abort logic if you need an upper bound on wait time. + + ```tsx title="React" import { useUserOperationReceipt } from "@worldcoin/minikit-react"; @@ -188,28 +192,51 @@ const { poll, isLoading, reset } = useUserOperationReceipt({ client }); const onClick = async () => { const result = await MiniKit.sendTransaction({...}); - const { receipt } = await poll(result.data.userOpHash); - // receipt contains the final transaction receipt + + try { + // Optionally race against your own timeout — poll() has no built-in one. + const { receipt } = await poll(result.data.userOpHash); + // receipt contains the final transaction receipt + } catch (error) { + // poll() throws if the user operation status resolves to "failed" + console.error("User operation failed:", error); + } }; ``` ```ts title="API" -type UserOperationStatusSuccess = { - status: "success"; - userOpHash: string; - sender: string; - transaction_hash: string; - nonce: string; -}; +type UserOperationStatus = + | { + status: "pending"; + } + | { + status: "success"; + userOpHash: string; + sender: string; + transaction_hash: string; + nonce: string; + } + | { + status: "failed"; + userOpHash: string; + error?: string; + }; const response = await fetch( `https://developer.world.org/api/v2/minikit/userop/${userOpHash}`, ); -const status = await response.json(); + +// The endpoint also returns 400/500 on a bad request or server error. +if (!response.ok) { + throw new Error(`Failed to fetch user operation status: ${response.status}`); +} + +const status: UserOperationStatus = await response.json(); if (status.status === "success") { - const success = status as UserOperationStatusSuccess; - console.log(success.transaction_hash); + console.log(status.transaction_hash); +} else if (status.status === "failed") { + console.error("User operation failed:", status.error); } ``` diff --git a/mini-apps/commands/share-contacts.mdx b/mini-apps/commands/share-contacts.mdx index db24bba..9aa4500 100644 --- a/mini-apps/commands/share-contacts.mdx +++ b/mini-apps/commands/share-contacts.mdx @@ -5,7 +5,7 @@ description: "Open the native contact picker using the unified MiniKit API." "twitter:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" --- -Use `MiniKit.shareContacts()` to open the World App contact picker. +Use `MiniKit.shareContacts()` to open the World App contact picker. `MiniKit.getContacts()` is also exported as an alias of `shareContacts()`—same options, result, and error codes. ## Basic Usage diff --git a/mini-apps/commands/sign-message.mdx b/mini-apps/commands/sign-message.mdx index 5f318b6..e3abf39 100644 --- a/mini-apps/commands/sign-message.mdx +++ b/mini-apps/commands/sign-message.mdx @@ -7,6 +7,10 @@ description: "Sign an EIP-191 message using the unified MiniKit API." Use `MiniKit.signMessage()` to request a personal signature from the user's wallet. + +**TypeScript setup**: `@worldcoin/minikit-js`'s shipped types reference `window.WorldApp` without declaring it, which fails under `"skipLibCheck": false`. Set `"skipLibCheck": true` in your `tsconfig.json` (or add your own `Window.WorldApp` shim) so the package's types compile. + + ## Basic Usage diff --git a/mini-apps/commands/wallet-auth.mdx b/mini-apps/commands/wallet-auth.mdx index f35bb74..e52a345 100644 --- a/mini-apps/commands/wallet-auth.mdx +++ b/mini-apps/commands/wallet-auth.mdx @@ -61,7 +61,7 @@ type MiniKitWalletAuthOptions = { expirationTime?: Date; notBefore?: Date; requestId?: string; - fallback?: () => unknown; + fallback?: () => Promise | unknown; }; ``` @@ -77,6 +77,7 @@ type WalletAuthResponse = address: string; message: string; signature: string; + version?: number; // Command protocol version (present when returned from World App) }; } | { @@ -99,7 +100,7 @@ type WalletAuthResponse = ## Backend Verification -Always verify the returned SIWE payload on your backend. +Always verify the returned SIWE payload on your backend. This example targets Next.js 15+, where `cookies()` returns a `Promise`. ```ts import { cookies } from "next/headers"; @@ -115,7 +116,8 @@ type RequestBody = { export async function POST(req: NextRequest) { const { payload, nonce } = (await req.json()) as RequestBody; - if (nonce !== cookies().get("siwe")?.value) { + const cookieStore = await cookies(); + if (nonce !== cookieStore.get("siwe")?.value) { return NextResponse.json( { isValid: false, error: "Invalid nonce" }, { status: 400 }, @@ -157,7 +159,7 @@ export async function POST(req: NextRequest) { | `expirationTime` | `Date` | No | When the SIWE message expires | | `notBefore` | `Date` | No | SIWE message is not valid before this time | | `requestId` | `string` | No | Arbitrary ID for correlating the request on your backend | -| `fallback` | `() => Promise` | No | Custom fallback for non-World-App environments | +| `fallback` | `() => Promise \| TFallback` | No | Custom fallback for non-World-App environments (sync or async) | ## Notes diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx index 01efed1..498f6ac 100644 --- a/mini-apps/growth/invites-viral.mdx +++ b/mini-apps/growth/invites-viral.mdx @@ -14,12 +14,16 @@ Referral programs are proven growth drivers because they leverage trust. People Create server-side invite pages that work across all platforms: -```typescript +```tsx // pages/invite.tsx +import { useEffect } from "react"; + +const appId = process.env.NEXT_PUBLIC_APP_ID; + export default function InvitePage({ code }: { code: string }) { useEffect(() => { // Redirect to mini app - window.location.href = `https://world.org/mini-app?app_id=${YOUR_APP_ID}&path=/invite?code=${code}` + window.location.href = `https://world.org/mini-app?app_id=${appId}&path=${encodeURIComponent(`/invite?code=${code}`)}` }, [code]) return
Redirecting to mini app...
@@ -34,9 +38,7 @@ Deep-link (opens World App directly if installed) `worldapp://mini-app?app_id={app_id}&path={path}` -To force opening in the device browser instead of the native webview, append `open_out_of_window=true` to the URL (works for both universal and deep links). - -Note: path should be URL encoded. +Note: path should be URL encoded. Rather than hand-building this URL, you can use the SDK helper `MiniKit.getMiniAppUrl(appId, path)`, which builds the `world.org` universal link and encodes the path for you. ### 2. Generate Share Links Create a shareable link that includes the referral information: @@ -53,7 +55,7 @@ function generateInviteLink(userId: string): string { ### 3. Implement Share Functionality -Add share buttons at key moments in your user journey: +Add share buttons at key moments in your user journey. Note: `trackEvent` below is a placeholder for whatever analytics tool you integrate yourself (Segment, PostHog, a custom endpoint, etc.)—it is not a MiniKit command. ```typescript import { MiniKit } from "@worldcoin/minikit-js"; @@ -107,17 +109,33 @@ function handleReferral() { Implement two-sided rewards that benefit both parties: +This sample requires the caller to already have a World-ID-verified session (see [Wallet Auth](/mini-apps/commands/wallet-auth)) and adds server-side self-referral and idempotency checks—never trust `newUserId`/`referrerCode` from an unauthenticated request body: + ```typescript // api/process-referral.ts export async function processReferral(data: { newUserId: string; referrerCode: string; }) { + // Require a verified session for newUserId (e.g. from your walletAuth backend check) + if (!(await isVerifiedSession(data.newUserId))) { + return { success: false, reason: "unverified_user" }; + } + const referrer = await getUserByCode(data.referrerCode); if (!referrer) { return { success: false, reason: "invalid_referrer" }; } + if (referrer.id === data.newUserId) { + return { success: false, reason: "self_referral" }; + } + + // Idempotency: only credit a given new user once, ever + if (await hasAlreadyBeenReferred(data.newUserId)) { + return { success: false, reason: "already_referred" }; + } + // Credit both users await Promise.all([ creditUser(referrer.id, { @@ -132,6 +150,8 @@ export async function processReferral(data: { }), ]); + await markAsReferred(data.newUserId, referrer.id); + return { success: true }; } ``` @@ -200,6 +220,10 @@ const events = { }; ``` + +If you're using the canonical [Core Event Set](/mini-apps/growth/analytics#2-core-event-set-6-lines-of-code) from the Data & Analytics guide, `invite_link_created`/`invite_link_clicked` above map to that guide's `invite_sent`/`invite_accepted` events for the Invite Acceptance Rate metric—pick one taxonomy and use it consistently. + + ### Key Metrics Dashboard - **Invite Conversion Rate**: (Signups from invites) / (Total invite links clicked) @@ -225,6 +249,6 @@ Test these variables to optimize your viral loop: 1. Implement universal links for your invite flow 2. Add share buttons after key user achievements -3. Set up two-sided rewards with World ID verification +3. Set up two-sided rewards, gated on World ID verification plus server-side self-referral and idempotency checks 4. Track invite metrics and run small A/B tests 5. Scale successful invite mechanics across more touchpoints diff --git a/mini-apps/growth/notifications.mdx b/mini-apps/growth/notifications.mdx index 9816a1d..fed90f6 100644 --- a/mini-apps/growth/notifications.mdx +++ b/mini-apps/growth/notifications.mdx @@ -12,21 +12,21 @@ Thoughtful, behavior‑based notifications keep users engaged long after they cl | -------------------- | ------------------------------------------------------------------- | | **Retention boost** | Targeted pushes can 2–3× day‑7 retention. | | **Free visibility** | ≥ 15 % open rate unlocks a persistent badge on your app icon. | -| **Strict standards** | < 10 % open = delivery paused for 7 days—quality is non‑negotiable. | +| **Strict standards** | < 15 % open = no home‑screen badge—quality is non‑negotiable. | ### 2 · Quality Thresholds | Open‑Rate (7‑day) | Platform Action | Your Next Step | | ----------------- | ----------------- | --------------------------------- | -| **< 10%** | Paused for 1 week | Audit triggers & copy immediately | -| **10%+** | Badge displayed | Maintain & iterate | +| **< 15%** | No badge | Audit triggers & copy immediately | +| **15%+** | Badge displayed | Maintain & iterate | | **25%+** | "Excellent" tier | Scale what works, test new ideas | ### 3 · Core Principles 1. **Trigger‑based > Broadcasts** – React to _user actions_ (wins, risks) instead of fixed schedules. 2. **Personalize** – Use `${username}` placeholder to personalize notifications with usernames. -3. **Copy rules** – ≤ 30‑char title, 1–2 emojis, clear value + curiosity gap. +3. **Copy rules** – ≤ 30‑char title, ≤ 200‑char message, 1–2 emojis, clear value + curiosity gap. ### 4 · Trigger Library @@ -45,6 +45,7 @@ Thoughtful, behavior‑based notifications keep users engaged long after they cl ### 6 · Copy Cheatsheet +- **Limits**: title ≤ 30 characters, message ≤ 200 characters (API-enforced). - **Lead with benefit**: "Earn 50 coins" beats "Check the app". - **Curiosity**: "Something new awaits …". - **Concrete numbers**: "30 s left" > "Hurry up". diff --git a/mini-apps/guidelines/app-guidelines.mdx b/mini-apps/guidelines/app-guidelines.mdx index 33bae64..ebca55c 100644 --- a/mini-apps/guidelines/app-guidelines.mdx +++ b/mini-apps/guidelines/app-guidelines.mdx @@ -179,7 +179,4 @@ npm install @worldcoin/mini-apps-ui-kit-react Learn more in the package README and Storybook: - [Package README](https://www.npmjs.com/package/@worldcoin/mini-apps-ui-kit-react?activeTab=readme) - href="https://www.npmjs.com/package/@worldcoin/mini-apps-ui-kit-react?activeTab=readme" - target="\_blank" - > Package README - [UI Kit Storybook](https://mini-apps-ui-kit.world.org) diff --git a/mini-apps/guidelines/features-and-guidelines.mdx b/mini-apps/guidelines/features-and-guidelines.mdx index 35c8017..e02c8c1 100644 --- a/mini-apps/guidelines/features-and-guidelines.mdx +++ b/mini-apps/guidelines/features-and-guidelines.mdx @@ -30,7 +30,7 @@ curl -X POST "https://developer.worldcoin.org/api/v2/minikit/send-notification" { "language": "en", "title": "title", - "message": "🧑‍🍳 We're cooking something special for you ${username}" + "message": "🧑‍🍳 We'\''re cooking something special for you ${username}" } ], "mini_app_path": "worldapp://mini-app?app_id=[app_id]&path=[path]" diff --git a/mini-apps/migration/minikit-v2.mdx b/mini-apps/migration/minikit-v2.mdx index 0350e43..ae458cf 100644 --- a/mini-apps/migration/minikit-v2.mdx +++ b/mini-apps/migration/minikit-v2.mdx @@ -13,7 +13,6 @@ MiniKit 2.x consolidates command handling around async `MiniKit` methods and rem - Response interface changed to `{ executedWith, data }` - Types and helpers moved to `@worldcoin/minikit-js/commands`, `@worldcoin/minikit-js/siwe`, and `@worldcoin/minikit-js/address-book` - `walletAuth` nonce validation is stricter and expects an alphanumeric nonce without hyphens -- `signTypedData` deprecated - `sendTransaction` now takes encoded calldata `transactions` and returns `userOpHash` - Permit2 switched from SignatureTransfer to AllowanceTransfer - Standard ERC-20 `approve()` calls are now allowed, and approval will be revoked after the transaction diff --git a/mini-apps/migration/standalone-dapp.mdx b/mini-apps/migration/standalone-dapp.mdx index c4a2485..4c325e0 100644 --- a/mini-apps/migration/standalone-dapp.mdx +++ b/mini-apps/migration/standalone-dapp.mdx @@ -18,7 +18,7 @@ MiniKit commands auto-detect the environment. Outside World App, they fall back ## Ask an agent Add this skill and ask your agent to convert your web app to a mini app using the steps outlined in this guide. ``` -npx skills add worldcoin/minikit-js miniapp-to-web +npx skills add worldcoin/minikit-js -s miniapp-to-web ``` ## 1. Install Dependencies @@ -85,7 +85,7 @@ export default function Providers({ children }: { children: React.ReactNode }) { ## World ID -No changes. [IDKit](https://docs.worldcoin.org/world-id/quick-start) is independent of the wallet layer. +No changes. [IDKit](/world-id/idkit/mini-apps) is independent of the wallet layer. ## Auth diff --git a/mini-apps/migration/web-to-miniapp.mdx b/mini-apps/migration/web-to-miniapp.mdx index 41db4c1..aad8494 100644 --- a/mini-apps/migration/web-to-miniapp.mdx +++ b/mini-apps/migration/web-to-miniapp.mdx @@ -10,7 +10,7 @@ Convert an existing Next.js web app that uses viem to work as a World App mini a ## Ask an agent Add this skill and ask your agent to convert your web app to a mini app using the steps outlined in this guide. ``` -npx skills add worldcoin/minikit-js web-to-miniapp +npx skills add worldcoin/minikit-js -s web-to-miniapp ``` ## 1. Install MiniKit diff --git a/mini-apps/more/community-tools-perks.mdx b/mini-apps/more/community-tools-perks.mdx index d83ec6e..fa2e228 100644 --- a/mini-apps/more/community-tools-perks.mdx +++ b/mini-apps/more/community-tools-perks.mdx @@ -8,12 +8,12 @@ Special perks and integrations from trusted providers supporting the World Mini - Pilot program rewarding qualifying Mini App developers based on verified - human usage. $300K USD equivalent in WLD over three months. + Ongoing rewards program for Mini App builders based on verified human + usage. Your Mini App must already be live in World App to apply. Foundation Grants supporting the World Network and novel mini apps (50M WLD diff --git a/mini-apps/quick-start/init.mdx b/mini-apps/quick-start/init.mdx index 0208eec..c7ce2fe 100644 --- a/mini-apps/quick-start/init.mdx +++ b/mini-apps/quick-start/init.mdx @@ -75,8 +75,8 @@ import { MiniKit } from "@worldcoin/minikit-js"; import type { MiniAppGetPermissionsSuccessPayload } from "@worldcoin/minikit-js/commands"; const result = await MiniKit.getPermissions(); -const permissions: MiniAppGetPermissionsSuccessPayload["permissions"] = - result.data.permissions; +const permissions = (result.data as MiniAppGetPermissionsSuccessPayload) + .permissions; ``` Notes: @@ -117,6 +117,23 @@ MiniKit normalizes the raw World App launch origin into: If you need the untransformed World App payload, read `window.WorldApp` directly. + +`@worldcoin/minikit-js` does not ship a `Window.WorldApp` type augmentation, so TypeScript will not know about this property out of the box. Declare it yourself, matching the shape below: + +```tsx +declare global { + interface Window { + WorldApp?: { + world_app_version: number; + device_os: "ios" | "android"; + // ...see the full shape in the Type tab below + [key: string]: unknown; + }; + } +} +``` + + ```tsx diff --git a/mini-apps/quick-start/installing.mdx b/mini-apps/quick-start/installing.mdx index e5d6884..55059e0 100644 --- a/mini-apps/quick-start/installing.mdx +++ b/mini-apps/quick-start/installing.mdx @@ -10,7 +10,7 @@ description: "Create a Mini App with the official template or install MiniKit-JS ## Quick Start The fastest way to get started is by using our template next-15 repository. -Run the following command and follow the instructions to create a new mini app. For cleanliness we recommend using `pnpm` as your package manager. +Run the following command and follow the instructions to create a new mini app. This installs dependencies with `npm`; if you prefer `pnpm`, see [Manual Installation](#manual-installation) below. ```bash npx @worldcoin/create-mini-app@latest my-first-mini-app @@ -93,6 +93,20 @@ import { MiniKit } from "@worldcoin/minikit-js"; console.log(MiniKit.isInstalled()); ``` +3. Or, in a React component, read the same state reactively with the `useMiniKit()` hook (this is what the official starter template itself uses): + +```tsx +import { useMiniKit } from "@worldcoin/minikit-js/minikit-provider"; + +export function AuthButton() { + const { isInstalled, user } = useMiniKit(); + + if (!isInstalled) return null; + + return {user?.username ?? "Not signed in"}; +} +``` + ## Build with AI The [World Docs MCP](/model-context-protocol/world-docs) lets any coding assistant search the World documentation to help you build your mini app. @@ -100,12 +114,13 @@ The [World Docs MCP](/model-context-protocol/world-docs) lets any coding assista ```bash - claude mcp add --transport http world https://docs.world.org/mcp + claude mcp add --transport http --scope project world-docs https://docs.world.org/mcp ``` + `--scope project` writes a shareable `.mcp.json` to your project (recommended); omit it and the server is instead registered only for you, in your global Claude config. ```bash - codex mcp add --transport http world https://docs.world.org/mcp + codex mcp add world-docs --url https://docs.world.org/mcp ``` @@ -113,7 +128,7 @@ The [World Docs MCP](/model-context-protocol/world-docs) lets any coding assista ```json { "mcpServers": { - "world": { + "world-docs": { "url": "https://docs.world.org/mcp" } } @@ -125,7 +140,7 @@ The [World Docs MCP](/model-context-protocol/world-docs) lets any coding assista ```json { "servers": { - "world": { + "world-docs": { "type": "http", "url": "https://docs.world.org/mcp" } diff --git a/mini-apps/quick-start/responses.mdx b/mini-apps/quick-start/responses.mdx index d314aab..c911eed 100644 --- a/mini-apps/quick-start/responses.mdx +++ b/mini-apps/quick-start/responses.mdx @@ -48,5 +48,20 @@ async function signInWithWallet() { ## Event Subscriptions -For new 2.x command integrations, prefer `await MiniKit.()`. +For new 2.x command integrations, prefer `await MiniKit.()`—it already resolves (or throws) with the result, so you don't need to listen for an event. + +MiniKit still exposes the lower-level event API it uses internally, in case you need to observe a response event directly (for example, to fan the same response out to multiple listeners): + +```tsx +import { MiniKit, ResponseEvent } from "@worldcoin/minikit-js"; + +MiniKit.subscribe(ResponseEvent.MiniAppWalletAuth, (payload) => { + console.log(payload); +}); + +// Later, to stop listening: +MiniKit.unsubscribe(ResponseEvent.MiniAppWalletAuth); +``` + +`MiniKit.trigger(event, payload)` dispatches an event to subscribers manually and is mainly useful for testing. For application code, `await MiniKit.()` remains the recommended pattern. diff --git a/mini-apps/quick-start/testing.mdx b/mini-apps/quick-start/testing.mdx index 24dc3d4..f380a0f 100644 --- a/mini-apps/quick-start/testing.mdx +++ b/mini-apps/quick-start/testing.mdx @@ -25,11 +25,11 @@ Your app id is in the developer portal in the format `app_xxxxxxxxxx`. */} - + - To test on a phone, open `https://worldcoin.org/mini-app?app_id=` (the docs page renders this link as a scannable QR code): + To test on a phone, open `https://world.org/mini-app?app_id=` (the docs page renders this link as a scannable QR code): 1. Build the URL with your App ID from the developer portal. 2. Scan the QR (or open the URL) with your phone’s camera. diff --git a/mini-apps/reference/address-book.mdx b/mini-apps/reference/address-book.mdx index 40c3084..881e310 100644 --- a/mini-apps/reference/address-book.mdx +++ b/mini-apps/reference/address-book.mdx @@ -23,8 +23,7 @@ const userWalletAddress = "0x000000000000000000000000000000000000dEaD" const isUserVerified = await getIsUserVerified(userWalletAddress) // optionally you can provide your rpc url as a second argument to the function ``` - Returns `true` if the address is verified -- Returns `false` if the address is not verified -- Throws an error if the verification check fails +- Returns `false` if the address is not verified, or if the verification check itself fails (errors are caught internally, logged to the console, and swallowed rather than thrown) ## React Bindings diff --git a/mini-apps/reference/credit-api.mdx b/mini-apps/reference/credit-api.mdx index c6313ce..abff652 100644 --- a/mini-apps/reference/credit-api.mdx +++ b/mini-apps/reference/credit-api.mdx @@ -19,6 +19,10 @@ https://credit.cash/api/borrower/[identifier] where `[identifier]` is either the user's wallet address or [World username](/mini-apps/reference/usernames). + +This endpoint requires authorization. Unauthenticated requests, including the example below, currently receive `401 Unauthorized`. Contact the Credit team (see [Support](#support)) to get access before integrating. + + The response is a JSON object with two keys: - `state`: the user's status on Credit @@ -80,6 +84,7 @@ curl -s "https://credit.cash/api/borrower/0x764C890E7D96481cBEa64c64C0F9cFF34bFF | **Status** | **Error** | **Description** | | ---------- | --------------------------- | -------------------------------------- | | 400 | Missing identifier | No wallet address or username provided | +| 401 | Unauthorized | Missing or invalid authorization | | 404 | Username not found | World username could not be resolved | | 500 | Failed to get borrower info | Internal server error | diff --git a/mini-apps/reference/status-page.mdx b/mini-apps/reference/status-page.mdx index dd714bb..adb510c 100644 --- a/mini-apps/reference/status-page.mdx +++ b/mini-apps/reference/status-page.mdx @@ -18,7 +18,7 @@ For transactions, status is determined by: ## Get Status - https://status.worldcoin.org/api/services + https://status.world.org/api/services This endpoint returns the current status of all World services. @@ -31,11 +31,11 @@ This endpoint returns the current status of all World services. ```bash cURL -curl -X GET "https://status.worldcoin.org/api/services?logs=true" +curl -X GET "https://status.world.org/api/services?logs=true" ``` ```javascript -fetch('https://status.worldcoin.org/api/services?logs=true') +fetch('https://status.world.org/api/services?logs=true') ``` diff --git a/mini-apps/reference/usernames.mdx b/mini-apps/reference/usernames.mdx index 1eb566c..4bd2885 100644 --- a/mini-apps/reference/usernames.mdx +++ b/mini-apps/reference/usernames.mdx @@ -25,4 +25,18 @@ Or you can request it manually, using the `getUserByAddress` method on MiniKit: const worldIdUser = await MiniKit.getUserByAddress(userAddress) ``` +You can also look a user up by their username with `getUserByUsername`: + +```tsx +const worldIdUser = await MiniKit.getUserByUsername(username) +``` + +Both methods resolve to the same shape: `{ walletAddress: string; username: string; profilePictureUrl: string; }`. + + +Neither method throws for an unknown address or username—the underlying request can 404, in which case the promise still resolves, with `walletAddress`, `username`, and `profilePictureUrl` all `undefined`. Check for a falsy `walletAddress` before using the result. + + +`MiniKit.getUserInfo` is also available as an alias of `getUserByAddress` (same signature, same behavior). + Other ways involve querying the [usernames service](https://usernames.worldcoin.org/docs). \ No newline at end of file diff --git a/mini-apps/sharing/add-money-qa.mdx b/mini-apps/sharing/add-money-qa.mdx index 1876cb6..43684a0 100644 --- a/mini-apps/sharing/add-money-qa.mdx +++ b/mini-apps/sharing/add-money-qa.mdx @@ -55,11 +55,11 @@ Url follows the schema below. Navigate there to use this Quick Action. ``` https://worldcoin.org/mini-app?app_id=app_e7d27c5ce2234e00558776f227f791ef -&path={%2Fbridge} -&toAddress={0xRecipientAddressHere} -&toToken={0xUSDCOrWLDAddress} -&amountUsd={100} -&sourceAppId={app_source1234567890abcdef} -&sourceAppName={My%20App} -&sourceDeeplinkPath={%2Fdashboard} +&path=%2Fbridge +&toAddress=0xRecipientAddressHere +&toToken=0xUSDCOrWLDAddress +&amountUsd=100 +&sourceAppId=app_source1234567890abcdef +&sourceAppName=My%20App +&sourceDeeplinkPath=%2Fdashboard ``` diff --git a/mini-apps/sharing/dna-qa.mdx b/mini-apps/sharing/dna-qa.mdx index 0dad6f3..02bb2cc 100644 --- a/mini-apps/sharing/dna-qa.mdx +++ b/mini-apps/sharing/dna-qa.mdx @@ -118,6 +118,7 @@ A string representing the complete deeplink URL to the DNA application with the ```typescript const deeplinkUrl = getDNADeeplinkUrl({ + tab: "send", fromToken: "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1", toToken: "0x4200000000000000000000000000000000000006", recipientAddress: "0xRecipientAddressHere", @@ -131,7 +132,7 @@ console.log(deeplinkUrl); ## **Generated Deeplink URL:** ```bash -https://worldcoin.org/mini-app?app_id=app_8e407cfbae7ae51c19b07faff837aeeb&path=%2Fwallet%3Ftab%3Dsend%26fromToken%3D0x79A02482A880bCE3F13e09Da970dC34db4CD24d1%26amount%3D1234500%26toToken%3D0x4200000000000000000000000000000000000006%26recipientAddress%3D0xRecipientAddressHere%26sourceAppId%3Dapp_a4f7f3e62c1de0b9490a5260cb390b56%26sourceDeeplinkPath%3D%252Fsome%252Fpath +https://worldcoin.org/mini-app?app_id=app_8e407cfbae7ae51c19b07faff837aeeb&path=%2Fwallet%3Ftab%3Dsend%26fromToken%3D0x79A02482A880bCE3F13e09Da970dC34db4CD24d1%26amount%3D1235%26toToken%3D0x4200000000000000000000000000000000000006%26recipientAddress%3D0xRecipientAddressHere%26sourceAppId%3Dapp_a4f7f3e62c1de0b9490a5260cb390b56%26sourceDeeplinkPath%3D%252Fpath ``` ## **Note** diff --git a/mini-apps/sharing/sage-qa.mdx b/mini-apps/sharing/sage-qa.mdx index 0ba8aaf..5289f0c 100644 --- a/mini-apps/sharing/sage-qa.mdx +++ b/mini-apps/sharing/sage-qa.mdx @@ -4,6 +4,10 @@ title: "Sage Support" "twitter:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" --- + +The [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) is currently unavailable (its Vercel deployment has been disabled), so the steps below cannot currently be completed. This page is deprecated until the dashboard is restored. + + [Sage](https://worldcoin.org/ecosystem/app_5dee2f19cd6eef599eb6ab275a0a7523) is an AI chatbot that lets users ask questions and get answers. Sage Support enables developers to integrate Sage chats seamlessly into their World Mini Apps. Developers get a white label version of Sage that acts as a support assistant for their Mini App using the context they give it. diff --git a/mini-apps/sharing/swap-qa.mdx b/mini-apps/sharing/swap-qa.mdx index 83a7b11..f180bad 100644 --- a/mini-apps/sharing/swap-qa.mdx +++ b/mini-apps/sharing/swap-qa.mdx @@ -93,5 +93,5 @@ console.log(link); ### Example link ``` -https://worldcoin.org/mini-app?app_id=app_6c5c5717c77abe83be8814c032c3a6f9&path=%2F%3FfromToken%3D0x2cFc85d8E48F8EAB294be644d9E25C3030863003 +https://worldcoin.org/mini-app?app_id=app_6c5c5717c77abe83be8814c032c3a6f9&path=%2F%3FfromToken%3D0x2cFc85d8E48F8EAB294be644d9E25C3030863003%26toToken%3D0x4200000000000000000000000000000000000006%26amount%3D1234500%26sourceAppId%3Dapp_source1234567890abcdef%26sourceAppName%3DMy%2520App ``` From f2a6933c55965e6b2843801391d72b850e4da480 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Mon, 21 Sep 2026 10:23:34 -0700 Subject: [PATCH 2/8] polish: tighten wording to match house style; reconcile Codex MCP command Trims over-explaining sentences the prior audit pass added (long justifying clauses, redundant caveats) to match this repo's terser Mintlify style, with no change to technical accuracy. Also reconciles the Codex MCP install command: model-context-protocol/ world-docs.mdx used the mcp-remote stdio-bridge form while this PR's mini-apps/quick-start/installing.mdx already used `codex mcp add world-docs --url ...` directly. `codex mcp add --help` (installed CLI 0.149.1) confirms native --url support for streamable HTTP servers, and this exact command is already registered and working locally (`transport: streamable_http`, enabled), so world-docs.mdx is updated to match installing.mdx, making the PR's "standardized to match" claim true. --- mini-apps/commands/pay.mdx | 2 +- mini-apps/commands/send-transaction.mdx | 4 ++-- mini-apps/commands/share-contacts.mdx | 2 +- mini-apps/commands/sign-message.mdx | 2 +- mini-apps/growth/invites-viral.mdx | 12 ++++++------ mini-apps/quick-start/init.mdx | 2 +- mini-apps/quick-start/installing.mdx | 6 +++--- mini-apps/quick-start/responses.mdx | 6 +++--- mini-apps/reference/address-book.mdx | 2 +- mini-apps/reference/credit-api.mdx | 2 +- mini-apps/reference/usernames.mdx | 2 +- mini-apps/sharing/sage-qa.mdx | 2 +- model-context-protocol/world-docs.mdx | 2 +- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/mini-apps/commands/pay.mdx b/mini-apps/commands/pay.mdx index 81b0533..5cd715b 100644 --- a/mini-apps/commands/pay.mdx +++ b/mini-apps/commands/pay.mdx @@ -9,7 +9,7 @@ This command is an abstraction for a simple transfer. This shouldn't be used out ## Supported Tokens -Import the `Tokens` enum rather than hardcoding symbol strings — note that some enum values differ from their display name (e.g. USDC's value is `"USDCE"`): +Import the `Tokens` enum instead of hardcoding symbol strings. Some enum values differ from their display name (e.g. `USDC` → `"USDCE"`): | `Tokens` member | Enum value | Currency | | --- | --- | --- | diff --git a/mini-apps/commands/send-transaction.mdx b/mini-apps/commands/send-transaction.mdx index a6abc55..26f4723 100644 --- a/mini-apps/commands/send-transaction.mdx +++ b/mini-apps/commands/send-transaction.mdx @@ -174,7 +174,7 @@ You can either use the `@worldcoin/minikit-react` hook or poll the Developer Por to check when the user operation is mined and get the final `transaction_hash`. -`poll()` runs an unbounded loop with no built-in timeout, and it **throws** (`Error("Transaction failed")`) if the user operation's status resolves to `failed`. Always call it inside a `try`/`catch`, and wrap it in your own timeout/abort logic if you need an upper bound on wait time. +`poll()` runs an unbounded loop with no built-in timeout, and it **throws** (`Error("Transaction failed")`) if the user operation's status resolves to `failed`. Call it inside a `try`/`catch`, and add your own timeout/abort logic for an upper bound on wait time. @@ -194,7 +194,7 @@ const onClick = async () => { const result = await MiniKit.sendTransaction({...}); try { - // Optionally race against your own timeout — poll() has no built-in one. + // poll() has no built-in timeout — race your own if needed. const { receipt } = await poll(result.data.userOpHash); // receipt contains the final transaction receipt } catch (error) { diff --git a/mini-apps/commands/share-contacts.mdx b/mini-apps/commands/share-contacts.mdx index 9aa4500..b6dc690 100644 --- a/mini-apps/commands/share-contacts.mdx +++ b/mini-apps/commands/share-contacts.mdx @@ -5,7 +5,7 @@ description: "Open the native contact picker using the unified MiniKit API." "twitter:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" --- -Use `MiniKit.shareContacts()` to open the World App contact picker. `MiniKit.getContacts()` is also exported as an alias of `shareContacts()`—same options, result, and error codes. +Use `MiniKit.shareContacts()` to open the World App contact picker. `MiniKit.getContacts()` is an alias of `shareContacts()`—same options, result, and error codes. ## Basic Usage diff --git a/mini-apps/commands/sign-message.mdx b/mini-apps/commands/sign-message.mdx index e3abf39..62af0bc 100644 --- a/mini-apps/commands/sign-message.mdx +++ b/mini-apps/commands/sign-message.mdx @@ -8,7 +8,7 @@ description: "Sign an EIP-191 message using the unified MiniKit API." Use `MiniKit.signMessage()` to request a personal signature from the user's wallet. -**TypeScript setup**: `@worldcoin/minikit-js`'s shipped types reference `window.WorldApp` without declaring it, which fails under `"skipLibCheck": false`. Set `"skipLibCheck": true` in your `tsconfig.json` (or add your own `Window.WorldApp` shim) so the package's types compile. +**TypeScript setup**: the shipped types reference `window.WorldApp` without declaring it, which fails under `"skipLibCheck": false`. Set `"skipLibCheck": true` in `tsconfig.json` (or add your own `Window.WorldApp` shim). ## Basic Usage diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx index 498f6ac..b5df788 100644 --- a/mini-apps/growth/invites-viral.mdx +++ b/mini-apps/growth/invites-viral.mdx @@ -38,7 +38,7 @@ Deep-link (opens World App directly if installed) `worldapp://mini-app?app_id={app_id}&path={path}` -Note: path should be URL encoded. Rather than hand-building this URL, you can use the SDK helper `MiniKit.getMiniAppUrl(appId, path)`, which builds the `world.org` universal link and encodes the path for you. +Note: path should be URL encoded — or use the SDK helper `MiniKit.getMiniAppUrl(appId, path)`, which builds and encodes the link for you. ### 2. Generate Share Links Create a shareable link that includes the referral information: @@ -55,7 +55,7 @@ function generateInviteLink(userId: string): string { ### 3. Implement Share Functionality -Add share buttons at key moments in your user journey. Note: `trackEvent` below is a placeholder for whatever analytics tool you integrate yourself (Segment, PostHog, a custom endpoint, etc.)—it is not a MiniKit command. +Add share buttons at key moments in your user journey. `trackEvent` below is a placeholder for your own analytics tool (Segment, PostHog, etc.), not a MiniKit command. ```typescript import { MiniKit } from "@worldcoin/minikit-js"; @@ -109,7 +109,7 @@ function handleReferral() { Implement two-sided rewards that benefit both parties: -This sample requires the caller to already have a World-ID-verified session (see [Wallet Auth](/mini-apps/commands/wallet-auth)) and adds server-side self-referral and idempotency checks—never trust `newUserId`/`referrerCode` from an unauthenticated request body: +Requires a World-ID-verified session (see [Wallet Auth](/mini-apps/commands/wallet-auth)). Never trust `newUserId`/`referrerCode` from an unauthenticated request—add server-side self-referral and idempotency checks: ```typescript // api/process-referral.ts @@ -117,7 +117,7 @@ export async function processReferral(data: { newUserId: string; referrerCode: string; }) { - // Require a verified session for newUserId (e.g. from your walletAuth backend check) + // Require a verified session for newUserId (e.g. via your walletAuth backend check) if (!(await isVerifiedSession(data.newUserId))) { return { success: false, reason: "unverified_user" }; } @@ -221,7 +221,7 @@ const events = { ``` -If you're using the canonical [Core Event Set](/mini-apps/growth/analytics#2-core-event-set-6-lines-of-code) from the Data & Analytics guide, `invite_link_created`/`invite_link_clicked` above map to that guide's `invite_sent`/`invite_accepted` events for the Invite Acceptance Rate metric—pick one taxonomy and use it consistently. +If you use the canonical [Core Event Set](/mini-apps/growth/analytics#2-core-event-set-6-lines-of-code), `invite_link_created`/`invite_link_clicked` map to `invite_sent`/`invite_accepted` there. Pick one taxonomy and use it consistently. ### Key Metrics Dashboard @@ -249,6 +249,6 @@ Test these variables to optimize your viral loop: 1. Implement universal links for your invite flow 2. Add share buttons after key user achievements -3. Set up two-sided rewards, gated on World ID verification plus server-side self-referral and idempotency checks +3. Set up two-sided rewards with World ID verification, self-referral, and idempotency checks 4. Track invite metrics and run small A/B tests 5. Scale successful invite mechanics across more touchpoints diff --git a/mini-apps/quick-start/init.mdx b/mini-apps/quick-start/init.mdx index c7ce2fe..35b112c 100644 --- a/mini-apps/quick-start/init.mdx +++ b/mini-apps/quick-start/init.mdx @@ -118,7 +118,7 @@ MiniKit normalizes the raw World App launch origin into: If you need the untransformed World App payload, read `window.WorldApp` directly. -`@worldcoin/minikit-js` does not ship a `Window.WorldApp` type augmentation, so TypeScript will not know about this property out of the box. Declare it yourself, matching the shape below: +`@worldcoin/minikit-js` doesn't ship a `Window.WorldApp` type. Declare it yourself, matching the shape below: ```tsx declare global { diff --git a/mini-apps/quick-start/installing.mdx b/mini-apps/quick-start/installing.mdx index 55059e0..fe238b3 100644 --- a/mini-apps/quick-start/installing.mdx +++ b/mini-apps/quick-start/installing.mdx @@ -10,7 +10,7 @@ description: "Create a Mini App with the official template or install MiniKit-JS ## Quick Start The fastest way to get started is by using our template next-15 repository. -Run the following command and follow the instructions to create a new mini app. This installs dependencies with `npm`; if you prefer `pnpm`, see [Manual Installation](#manual-installation) below. +Run the following command and follow the instructions to create a new mini app. This installs dependencies with `npm`; if you prefer `pnpm`, see [Manual Installation](#manual-installation). ```bash npx @worldcoin/create-mini-app@latest my-first-mini-app @@ -93,7 +93,7 @@ import { MiniKit } from "@worldcoin/minikit-js"; console.log(MiniKit.isInstalled()); ``` -3. Or, in a React component, read the same state reactively with the `useMiniKit()` hook (this is what the official starter template itself uses): +3. Or, in a React component, read the same state reactively with the `useMiniKit()` hook, used by the official starter template: ```tsx import { useMiniKit } from "@worldcoin/minikit-js/minikit-provider"; @@ -116,7 +116,7 @@ The [World Docs MCP](/model-context-protocol/world-docs) lets any coding assista ```bash claude mcp add --transport http --scope project world-docs https://docs.world.org/mcp ``` - `--scope project` writes a shareable `.mcp.json` to your project (recommended); omit it and the server is instead registered only for you, in your global Claude config. + `--scope project` writes a shareable `.mcp.json` to your project (recommended). Omit it to register the server only for you, in your global Claude config. ```bash diff --git a/mini-apps/quick-start/responses.mdx b/mini-apps/quick-start/responses.mdx index c911eed..5b37b2a 100644 --- a/mini-apps/quick-start/responses.mdx +++ b/mini-apps/quick-start/responses.mdx @@ -48,9 +48,9 @@ async function signInWithWallet() { ## Event Subscriptions -For new 2.x command integrations, prefer `await MiniKit.()`—it already resolves (or throws) with the result, so you don't need to listen for an event. +For new 2.x command integrations, prefer `await MiniKit.()`; it resolves (or throws) directly, no event listener needed. -MiniKit still exposes the lower-level event API it uses internally, in case you need to observe a response event directly (for example, to fan the same response out to multiple listeners): +MiniKit still exposes its lower-level event API for observing a response event directly (e.g. to fan the response out to multiple listeners): ```tsx import { MiniKit, ResponseEvent } from "@worldcoin/minikit-js"; @@ -63,5 +63,5 @@ MiniKit.subscribe(ResponseEvent.MiniAppWalletAuth, (payload) => { MiniKit.unsubscribe(ResponseEvent.MiniAppWalletAuth); ``` -`MiniKit.trigger(event, payload)` dispatches an event to subscribers manually and is mainly useful for testing. For application code, `await MiniKit.()` remains the recommended pattern. +`MiniKit.trigger(event, payload)` dispatches an event to subscribers manually, mainly for testing. Prefer `await MiniKit.()` in application code. diff --git a/mini-apps/reference/address-book.mdx b/mini-apps/reference/address-book.mdx index 881e310..bee7185 100644 --- a/mini-apps/reference/address-book.mdx +++ b/mini-apps/reference/address-book.mdx @@ -23,7 +23,7 @@ const userWalletAddress = "0x000000000000000000000000000000000000dEaD" const isUserVerified = await getIsUserVerified(userWalletAddress) // optionally you can provide your rpc url as a second argument to the function ``` - Returns `true` if the address is verified -- Returns `false` if the address is not verified, or if the verification check itself fails (errors are caught internally, logged to the console, and swallowed rather than thrown) +- Returns `false` if the address is not verified, or if the check itself fails (errors are caught and logged, not thrown) ## React Bindings diff --git a/mini-apps/reference/credit-api.mdx b/mini-apps/reference/credit-api.mdx index abff652..12d57a7 100644 --- a/mini-apps/reference/credit-api.mdx +++ b/mini-apps/reference/credit-api.mdx @@ -20,7 +20,7 @@ https://credit.cash/api/borrower/[identifier] where `[identifier]` is either the user's wallet address or [World username](/mini-apps/reference/usernames). -This endpoint requires authorization. Unauthenticated requests, including the example below, currently receive `401 Unauthorized`. Contact the Credit team (see [Support](#support)) to get access before integrating. +This endpoint requires authorization; unauthenticated requests, including the example below, receive `401 Unauthorized`. Contact the [Credit team](#support) for access before integrating. The response is a JSON object with two keys: diff --git a/mini-apps/reference/usernames.mdx b/mini-apps/reference/usernames.mdx index 4bd2885..9347c32 100644 --- a/mini-apps/reference/usernames.mdx +++ b/mini-apps/reference/usernames.mdx @@ -34,7 +34,7 @@ const worldIdUser = await MiniKit.getUserByUsername(username) Both methods resolve to the same shape: `{ walletAddress: string; username: string; profilePictureUrl: string; }`. -Neither method throws for an unknown address or username—the underlying request can 404, in which case the promise still resolves, with `walletAddress`, `username`, and `profilePictureUrl` all `undefined`. Check for a falsy `walletAddress` before using the result. +Neither method throws for an unknown address or username. A 404 still resolves the promise, with `walletAddress`, `username`, and `profilePictureUrl` all `undefined`. Check for a falsy `walletAddress` before using the result. `MiniKit.getUserInfo` is also available as an alias of `getUserByAddress` (same signature, same behavior). diff --git a/mini-apps/sharing/sage-qa.mdx b/mini-apps/sharing/sage-qa.mdx index 5289f0c..735f22a 100644 --- a/mini-apps/sharing/sage-qa.mdx +++ b/mini-apps/sharing/sage-qa.mdx @@ -5,7 +5,7 @@ title: "Sage Support" --- -The [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) is currently unavailable (its Vercel deployment has been disabled), so the steps below cannot currently be completed. This page is deprecated until the dashboard is restored. +The [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) is unavailable (its Vercel deployment is disabled), so the steps below cannot be completed. This page is deprecated until the dashboard is restored. [Sage](https://worldcoin.org/ecosystem/app_5dee2f19cd6eef599eb6ab275a0a7523) is an AI chatbot that lets users ask questions and get answers. diff --git a/model-context-protocol/world-docs.mdx b/model-context-protocol/world-docs.mdx index d3c4817..4e3b4b7 100644 --- a/model-context-protocol/world-docs.mdx +++ b/model-context-protocol/world-docs.mdx @@ -28,7 +28,7 @@ The docs MCP does not require authentication. ```bash - codex mcp add world-docs -- npx -y mcp-remote https://docs.world.org/mcp --transport http-only + codex mcp add world-docs --url https://docs.world.org/mcp ``` From 6d6f1e4649fe7051a646239855464b44a2441299 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Mon, 21 Sep 2026 16:03:55 -0700 Subject: [PATCH 3/8] chore: add missing Tokens enum symbols to cspell dictionary Fixes the spellcheck CI failure on this PR. --- cspell.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cspell.json b/cspell.json index 61daf8c..eeb5e11 100644 --- a/cspell.json +++ b/cspell.json @@ -43,6 +43,7 @@ "efgh", "erigon", "están", + "EURC", "Ethereum", "ethersproject", "fdir", @@ -167,6 +168,7 @@ "unlinkable", "unpackedProof", "urlencode", + "USDCE", "userop", "UUPS", "uvwx", @@ -176,7 +178,12 @@ "vuni", "wagmi", "walletauth", + "WBRL", + "WCLP", + "WCOP", "wcsep", + "WMXN", + "WPEN", "webview", "worldapp", "worldchain", From 37b0f6d4cd17143418c3a0ec0617b96630eb4eb7 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Mon, 21 Sep 2026 16:32:13 -0700 Subject: [PATCH 4/8] fix: address 4 Codex review findings on PR #198 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - growth/invites-viral.mdx (P1): stop treating a Wallet Auth session as World ID verification — SIWE only proves wallet control, not personhood, so the referral bonus could be farmed across wallets. Now requires a stored IDKit-verified nullifier instead. - growth/invites-viral.mdx (P1): referral claiming had a check-then-act race (hasAlreadyBeenReferred then credit) that let concurrent requests double-credit. Replaced with an atomic claim (claimReferral, e.g. INSERT ... ON CONFLICT DO NOTHING on a unique column) performed before crediting. - commands/send-transaction.mdx (P2): the documented "failed" status shape invented an `error` field the real GetUserOperationResponse schema (openapi/developer-portal.json) doesn't have, while omitting its real (nullable) sender/transaction_hash/nonce fields. Fixed to match the real schema. - quick-start/init.mdx (P2): the window.WorldApp augmentation snippet used a bare `declare global` block, which fails with TS2669 when copied into a real global.d.ts file (not a module). Added the required `export {};`, verified with tsc that it now compiles. --- mini-apps/commands/send-transaction.mdx | 6 ++++-- mini-apps/growth/invites-viral.mdx | 19 +++++++++++-------- mini-apps/quick-start/init.mdx | 4 +++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/mini-apps/commands/send-transaction.mdx b/mini-apps/commands/send-transaction.mdx index 26f4723..397414b 100644 --- a/mini-apps/commands/send-transaction.mdx +++ b/mini-apps/commands/send-transaction.mdx @@ -219,7 +219,9 @@ type UserOperationStatus = | { status: "failed"; userOpHash: string; - error?: string; + sender: string | null; + transaction_hash: string | null; + nonce: string | null; }; const response = await fetch( @@ -236,7 +238,7 @@ const status: UserOperationStatus = await response.json(); if (status.status === "success") { console.log(status.transaction_hash); } else if (status.status === "failed") { - console.error("User operation failed:", status.error); + console.error("User operation failed:", status.userOpHash); } ``` diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx index b5df788..6e6c031 100644 --- a/mini-apps/growth/invites-viral.mdx +++ b/mini-apps/growth/invites-viral.mdx @@ -109,7 +109,7 @@ function handleReferral() { Implement two-sided rewards that benefit both parties: -Requires a World-ID-verified session (see [Wallet Auth](/mini-apps/commands/wallet-auth)). Never trust `newUserId`/`referrerCode` from an unauthenticated request—add server-side self-referral and idempotency checks: +Requires a World-ID-verified user (an [IDKit](/world-id/idkit/integrate) proof, not [Wallet Auth](/mini-apps/commands/wallet-auth) — SIWE only proves wallet control, not personhood, so gating on it alone lets one person collect the bonus from many wallets). Never trust `newUserId`/`referrerCode` from an unauthenticated request, and claim the referral atomically so concurrent requests can't double-credit it: ```typescript // api/process-referral.ts @@ -117,8 +117,9 @@ export async function processReferral(data: { newUserId: string; referrerCode: string; }) { - // Require a verified session for newUserId (e.g. via your walletAuth backend check) - if (!(await isVerifiedSession(data.newUserId))) { + // Require a stored World ID nullifier for newUserId, set when they verified + // with IDKit — not a Wallet Auth session, which only proves wallet control. + if (!(await hasVerifiedWorldId(data.newUserId))) { return { success: false, reason: "unverified_user" }; } @@ -131,12 +132,16 @@ export async function processReferral(data: { return { success: false, reason: "self_referral" }; } - // Idempotency: only credit a given new user once, ever - if (await hasAlreadyBeenReferred(data.newUserId)) { + // Atomically claim the referral (e.g. INSERT ... ON CONFLICT DO NOTHING on + // a unique referred_user_id column) before crediting anything. If two + // requests race, only one INSERT succeeds. + const claimed = await claimReferral(data.newUserId, referrer.id); + if (!claimed) { return { success: false, reason: "already_referred" }; } - // Credit both users + // Credit both users. If a credit fails after the claim succeeds, retry the + // credit step alone — the unique claim already prevents a duplicate credit. await Promise.all([ creditUser(referrer.id, { type: "referral_bonus", @@ -150,8 +155,6 @@ export async function processReferral(data: { }), ]); - await markAsReferred(data.newUserId, referrer.id); - return { success: true }; } ``` diff --git a/mini-apps/quick-start/init.mdx b/mini-apps/quick-start/init.mdx index 35b112c..fd30fdb 100644 --- a/mini-apps/quick-start/init.mdx +++ b/mini-apps/quick-start/init.mdx @@ -118,9 +118,11 @@ MiniKit normalizes the raw World App launch origin into: If you need the untransformed World App payload, read `window.WorldApp` directly. -`@worldcoin/minikit-js` doesn't ship a `Window.WorldApp` type. Declare it yourself, matching the shape below: +`@worldcoin/minikit-js` doesn't ship a `Window.WorldApp` type. Declare it yourself in a `global.d.ts` file, matching the shape below: ```tsx +export {}; // required so this file is a module and `declare global` is valid + declare global { interface Window { WorldApp?: { From de60f108cd098bc2f665f1044af8cccc3a680f5a Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Tue, 22 Sep 2026 18:45:35 -0700 Subject: [PATCH 5/8] docs(mini-apps): align examples with released MiniKit API --- mini-apps/commands/request-permission.mdx | 6 ++++-- mini-apps/commands/share-contacts.mdx | 2 +- mini-apps/growth/invites-viral.mdx | 2 +- mini-apps/quick-start/installing.mdx | 6 +++--- mini-apps/quick-start/responses.mdx | 8 +++++--- mini-apps/reference/usernames.mdx | 11 ++++++++--- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/mini-apps/commands/request-permission.mdx b/mini-apps/commands/request-permission.mdx index 160cd19..cbe0d00 100644 --- a/mini-apps/commands/request-permission.mdx +++ b/mini-apps/commands/request-permission.mdx @@ -81,6 +81,8 @@ Define a custom fallback in the command payload for support outside mini apps. ## Error Codes +The SDK's `RequestPermissionErrorCodes` enum defines these errors: + | Code | Meaning | | --- | --- | | `user_rejected` | The user rejected the request | @@ -89,8 +91,8 @@ Define a custom fallback in the command payload for support outside mini apps. | `permission_disabled` | The permission is disabled | | `already_granted` | The permission is already granted | | `unsupported_permission` | The permission is not supported | -| `world_app_permission_not_enabled` | The permission is disabled at the World App level; prompt the user to enable it in World App settings | -| `mini_app_permission_not_enabled` | The permission is disabled at the mini app level; prompt the user to enable it in World App settings | + +For microphone access, the SDK also handles `world_app_permission_not_enabled` and `mini_app_permission_not_enabled` on the separate `MiniAppMicrophone` response event. These are not members of `RequestPermissionErrorCodes`; see the [microphone guide](/mini-apps/reference/microphone) for the microphone permission flow. ## Preview diff --git a/mini-apps/commands/share-contacts.mdx b/mini-apps/commands/share-contacts.mdx index b6dc690..6b972a6 100644 --- a/mini-apps/commands/share-contacts.mdx +++ b/mini-apps/commands/share-contacts.mdx @@ -5,7 +5,7 @@ description: "Open the native contact picker using the unified MiniKit API." "twitter:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" --- -Use `MiniKit.shareContacts()` to open the World App contact picker. `MiniKit.getContacts()` is an alias of `shareContacts()`—same options, result, and error codes. +Use `MiniKit.shareContacts()` to open the World App contact picker. The named `getContacts` export from `@worldcoin/minikit-js/commands` is an alias of that module's `shareContacts` function, with the same options, result, and error codes. ## Basic Usage diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx index 6e6c031..3bd22b2 100644 --- a/mini-apps/growth/invites-viral.mdx +++ b/mini-apps/growth/invites-viral.mdx @@ -224,7 +224,7 @@ const events = { ``` -If you use the canonical [Core Event Set](/mini-apps/growth/analytics#2-core-event-set-6-lines-of-code), `invite_link_created`/`invite_link_clicked` map to `invite_sent`/`invite_accepted` there. Pick one taxonomy and use it consistently. +If you use the canonical [Core Event Set](/mini-apps/growth/analytics#2-%C2%B7-core-event-set-6-lines-of-code), `invite_link_created`/`invite_link_clicked` map to `invite_sent`/`invite_accepted` there. Pick one taxonomy and use it consistently. ### Key Metrics Dashboard diff --git a/mini-apps/quick-start/installing.mdx b/mini-apps/quick-start/installing.mdx index fe238b3..a963d30 100644 --- a/mini-apps/quick-start/installing.mdx +++ b/mini-apps/quick-start/installing.mdx @@ -98,12 +98,12 @@ console.log(MiniKit.isInstalled()); ```tsx import { useMiniKit } from "@worldcoin/minikit-js/minikit-provider"; -export function AuthButton() { - const { isInstalled, user } = useMiniKit(); +export function MiniKitStatus() { + const { isInstalled } = useMiniKit(); if (!isInstalled) return null; - return {user?.username ?? "Not signed in"}; + return MiniKit is installed; } ``` diff --git a/mini-apps/quick-start/responses.mdx b/mini-apps/quick-start/responses.mdx index 5b37b2a..ed1fcfa 100644 --- a/mini-apps/quick-start/responses.mdx +++ b/mini-apps/quick-start/responses.mdx @@ -50,10 +50,11 @@ async function signInWithWallet() { For new 2.x command integrations, prefer `await MiniKit.()`; it resolves (or throws) directly, no event listener needed. -MiniKit still exposes its lower-level event API for observing a response event directly (e.g. to fan the response out to multiple listeners): +MiniKit also exposes a lower-level event API with one handler per response event. A new subscription replaces the previous handler. Use it only when managing the low-level response flow yourself: ```tsx -import { MiniKit, ResponseEvent } from "@worldcoin/minikit-js"; +import { MiniKit } from "@worldcoin/minikit-js"; +import { ResponseEvent } from "@worldcoin/minikit-js/commands"; MiniKit.subscribe(ResponseEvent.MiniAppWalletAuth, (payload) => { console.log(payload); @@ -63,5 +64,6 @@ MiniKit.subscribe(ResponseEvent.MiniAppWalletAuth, (payload) => { MiniKit.unsubscribe(ResponseEvent.MiniAppWalletAuth); ``` -`MiniKit.trigger(event, payload)` dispatches an event to subscribers manually, mainly for testing. Prefer `await MiniKit.()` in application code. +Commands register their response handlers with the same event manager. Do not use subscriptions to observe a concurrent `MiniKit.()` call: registrations can replace each other, and unsubscribing can prevent the command promise from resolving. Handle the awaited result instead. +`MiniKit.trigger(event, payload)` invokes the event's current handler manually, mainly for testing. diff --git a/mini-apps/reference/usernames.mdx b/mini-apps/reference/usernames.mdx index 9347c32..3b4c7af 100644 --- a/mini-apps/reference/usernames.mdx +++ b/mini-apps/reference/usernames.mdx @@ -31,12 +31,17 @@ You can also look a user up by their username with `getUserByUsername`: const worldIdUser = await MiniKit.getUserByUsername(username) ``` -Both methods resolve to the same shape: `{ walletAddress: string; username: string; profilePictureUrl: string; }`. +Both methods return an object with `walletAddress`, `username`, and `profilePictureUrl` properties. The SDK type marks `username` and `profilePictureUrl` as optional; callers must also handle `null` profile values at runtime. -Neither method throws for an unknown address or username. A 404 still resolves the promise, with `walletAddress`, `username`, and `profilePictureUrl` all `undefined`. Check for a falsy `walletAddress` before using the result. +The two lookup methods handle unknown users differently: + +- `getUserByAddress(address)` preserves the supplied `walletAddress`. When the service returns no profile, `username` and `profilePictureUrl` are `null`. +- `getUserByUsername(username)` maps the service's unknown-user 404 response to an object whose properties are `undefined`. + +Check for a username before using profile data; a truthy `walletAddress` alone does not mean a profile exists. These unknown-user responses resolve normally, but network or JSON parsing failures can still reject the promise. `MiniKit.getUserInfo` is also available as an alias of `getUserByAddress` (same signature, same behavior). -Other ways involve querying the [usernames service](https://usernames.worldcoin.org/docs). \ No newline at end of file +Other ways involve querying the [usernames service](https://usernames.worldcoin.org/docs). From 28226bfa5a65e82d8851c4a832ffae7ea9ae4c87 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Wed, 23 Sep 2026 14:59:18 -0700 Subject: [PATCH 6/8] docs(mini-apps): address verification findings - pay, chat, wallet-auth: fallback types back to `() => unknown` to match the other command pages; pay: drop unused import, only USDC's enum value differs - share-contacts: getContacts is a legacy alias; call MiniKit.shareContacts() - send-transaction: only Error("Transaction failed") means the user op failed; document fetch/abort rejections, reset(), and timeout scope; add pending fields - invites-viral: drop getMiniAppUrl tip, re-add a scoped open_out_of_window line, read the code param, per-credit idempotency keys, fix Core Event Set mapping - minikit-v2: restore the signTypedData deprecation bullet (2.0.0 CHANGELOG) - installing: pnpm via --no-install, skipLibCheck note moved here from sign-message, unnumber the useMiniKit step, correct Claude Code scope wording - notifications: 16-char title limit with ${username}; request-permission: align with microphone guide; wallet-auth: version in example; responses: alphanumeric nonce; sage-qa: shorter warning --- mini-apps/commands/chat.mdx | 2 +- mini-apps/commands/pay.mdx | 5 ++--- mini-apps/commands/request-permission.mdx | 2 +- mini-apps/commands/send-transaction.mdx | 18 ++++++++++++++---- mini-apps/commands/share-contacts.mdx | 2 +- mini-apps/commands/sign-message.mdx | 4 ---- mini-apps/commands/wallet-auth.mdx | 7 ++++--- mini-apps/growth/invites-viral.mdx | 15 ++++++++++----- mini-apps/growth/notifications.mdx | 2 +- mini-apps/migration/minikit-v2.mdx | 1 + mini-apps/quick-start/installing.mdx | 10 +++++++--- mini-apps/quick-start/responses.mdx | 2 +- mini-apps/sharing/sage-qa.mdx | 2 +- 13 files changed, 44 insertions(+), 28 deletions(-) diff --git a/mini-apps/commands/chat.mdx b/mini-apps/commands/chat.mdx index 6907aac..754de44 100644 --- a/mini-apps/commands/chat.mdx +++ b/mini-apps/commands/chat.mdx @@ -38,7 +38,7 @@ export async function shareToChat() { type MiniKitChatOptions = { message: string; to?: string[]; - fallback?: () => Promise | MiniAppChatSuccessPayload; + fallback?: () => unknown; }; ``` diff --git a/mini-apps/commands/pay.mdx b/mini-apps/commands/pay.mdx index 5cd715b..4a98992 100644 --- a/mini-apps/commands/pay.mdx +++ b/mini-apps/commands/pay.mdx @@ -9,7 +9,7 @@ This command is an abstraction for a simple transfer. This shouldn't be used out ## Supported Tokens -Import the `Tokens` enum instead of hardcoding symbol strings. Some enum values differ from their display name (e.g. `USDC` → `"USDCE"`): +Import the `Tokens` enum instead of hardcoding symbol strings. Only `Tokens.USDC` has a value that differs from its name (`"USDCE"`): | `Tokens` member | Enum value | Currency | | --- | --- | --- | @@ -31,7 +31,6 @@ import { MiniKit } from "@worldcoin/minikit-js"; import { Tokens, tokenToDecimals, - type MiniKitPayOptions, } from "@worldcoin/minikit-js/commands"; export async function sendPayment() { @@ -76,7 +75,7 @@ type MiniKitPayOptions = { }[]; description: string; network?: Network; // Optional; currently only "worldchain" and defaults to World Chain - fallback?: () => Promise | PayResult; + fallback?: () => unknown; }; ``` diff --git a/mini-apps/commands/request-permission.mdx b/mini-apps/commands/request-permission.mdx index cbe0d00..b30b012 100644 --- a/mini-apps/commands/request-permission.mdx +++ b/mini-apps/commands/request-permission.mdx @@ -92,7 +92,7 @@ The SDK's `RequestPermissionErrorCodes` enum defines these errors: | `already_granted` | The permission is already granted | | `unsupported_permission` | The permission is not supported | -For microphone access, the SDK also handles `world_app_permission_not_enabled` and `mini_app_permission_not_enabled` on the separate `MiniAppMicrophone` response event. These are not members of `RequestPermissionErrorCodes`; see the [microphone guide](/mini-apps/reference/microphone) for the microphone permission flow. +For microphone access, `permission_disabled` or `world_app_permission_not_enabled` means the user must first enable the microphone for World App; see the [microphone guide](/mini-apps/reference/microphone). The SDK types `world_app_permission_not_enabled` and `mini_app_permission_not_enabled` on the separate `MiniAppMicrophone` response event, not in `RequestPermissionErrorCodes`. ## Preview diff --git a/mini-apps/commands/send-transaction.mdx b/mini-apps/commands/send-transaction.mdx index 397414b..2d389dd 100644 --- a/mini-apps/commands/send-transaction.mdx +++ b/mini-apps/commands/send-transaction.mdx @@ -174,7 +174,7 @@ You can either use the `@worldcoin/minikit-react` hook or poll the Developer Por to check when the user operation is mined and get the final `transaction_hash`. -`poll()` runs an unbounded loop with no built-in timeout, and it **throws** (`Error("Transaction failed")`) if the user operation's status resolves to `failed`. Call it inside a `try`/`catch`, and add your own timeout/abort logic for an upper bound on wait time. +`poll()` rejects with `Error("Transaction failed")` only when the user operation's status is `failed`. Other rejections don't mean the transaction failed: a status request failed (HTTP or network error), the receipt wait failed or timed out, or polling stopped with an `AbortError` because you called `reset()`, the component unmounted, or a new `poll()` started. Status polling has no overall timeout (an unknown hash stays `pending`); the hook's `timeout` option only bounds the receipt wait once a transaction hash exists. Call `reset()` to stop polling. @@ -194,12 +194,18 @@ const onClick = async () => { const result = await MiniKit.sendTransaction({...}); try { - // poll() has no built-in timeout — race your own if needed. const { receipt } = await poll(result.data.userOpHash); // receipt contains the final transaction receipt } catch (error) { - // poll() throws if the user operation status resolves to "failed" - console.error("User operation failed:", error); + if (error instanceof Error && error.message === "Transaction failed") { + // Only this error means the user operation failed + console.error("User operation failed"); + } else if (error instanceof Error && error.name === "AbortError") { + // Stopped by reset(), unmount, or a newer poll(); not a failure + } else { + // A status or receipt lookup failed; the outcome is unknown, so poll again + console.error("Could not confirm the user operation:", error); + } } }; ``` @@ -208,6 +214,10 @@ const onClick = async () => { type UserOperationStatus = | { status: "pending"; + userOpHash: string; + sender: null; + transaction_hash: null; + nonce: null; } | { status: "success"; diff --git a/mini-apps/commands/share-contacts.mdx b/mini-apps/commands/share-contacts.mdx index 6b972a6..86db5ce 100644 --- a/mini-apps/commands/share-contacts.mdx +++ b/mini-apps/commands/share-contacts.mdx @@ -5,7 +5,7 @@ description: "Open the native contact picker using the unified MiniKit API." "twitter:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" --- -Use `MiniKit.shareContacts()` to open the World App contact picker. The named `getContacts` export from `@worldcoin/minikit-js/commands` is an alias of that module's `shareContacts` function, with the same options, result, and error codes. +Use `MiniKit.shareContacts()` to open the World App contact picker. The `getContacts` export from `@worldcoin/minikit-js/commands` is a legacy alias; call `MiniKit.shareContacts()` instead, since the standalone export never receives World App's response. ## Basic Usage diff --git a/mini-apps/commands/sign-message.mdx b/mini-apps/commands/sign-message.mdx index 62af0bc..5f318b6 100644 --- a/mini-apps/commands/sign-message.mdx +++ b/mini-apps/commands/sign-message.mdx @@ -7,10 +7,6 @@ description: "Sign an EIP-191 message using the unified MiniKit API." Use `MiniKit.signMessage()` to request a personal signature from the user's wallet. - -**TypeScript setup**: the shipped types reference `window.WorldApp` without declaring it, which fails under `"skipLibCheck": false`. Set `"skipLibCheck": true` in `tsconfig.json` (or add your own `Window.WorldApp` shim). - - ## Basic Usage diff --git a/mini-apps/commands/wallet-auth.mdx b/mini-apps/commands/wallet-auth.mdx index e52a345..aa18af6 100644 --- a/mini-apps/commands/wallet-auth.mdx +++ b/mini-apps/commands/wallet-auth.mdx @@ -61,7 +61,7 @@ type MiniKitWalletAuthOptions = { expirationTime?: Date; notBefore?: Date; requestId?: string; - fallback?: () => Promise | unknown; + fallback?: () => unknown; }; ``` @@ -92,7 +92,8 @@ type WalletAuthResponse = "data": { "address": "0x1234567890123456789012345678901234567890", "message": "example.com wants you to sign in with your Ethereum account", - "signature": "0xabcdef1234567890" + "signature": "0xabcdef1234567890", + "version": 2 } } ``` @@ -159,7 +160,7 @@ export async function POST(req: NextRequest) { | `expirationTime` | `Date` | No | When the SIWE message expires | | `notBefore` | `Date` | No | SIWE message is not valid before this time | | `requestId` | `string` | No | Arbitrary ID for correlating the request on your backend | -| `fallback` | `() => Promise \| TFallback` | No | Custom fallback for non-World-App environments (sync or async) | +| `fallback` | `() => unknown` | No | Custom fallback for non-World-App environments (sync or async) | ## Notes diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx index 3bd22b2..8e3055a 100644 --- a/mini-apps/growth/invites-viral.mdx +++ b/mini-apps/growth/invites-viral.mdx @@ -38,7 +38,9 @@ Deep-link (opens World App directly if installed) `worldapp://mini-app?app_id={app_id}&path={path}` -Note: path should be URL encoded — or use the SDK helper `MiniKit.getMiniAppUrl(appId, path)`, which builds and encodes the link for you. +To open a link from your mini app in the device browser instead of World App, add `open_out_of_window=true` to its URL, e.g. `window.open("https://example.com/?open_out_of_window=true", "_blank")`. + +Note: path should be URL encoded. ### 2. Generate Share Links Create a shareable link that includes the referral information: @@ -89,7 +91,7 @@ Process referral codes when new users sign up: // On app initialization function handleReferral() { const urlParams = new URLSearchParams(window.location.search); - const refCode = urlParams.get("ref"); + const refCode = urlParams.get("code"); if (refCode && !currentUser.referredBy) { // Credit the referrer @@ -140,18 +142,21 @@ export async function processReferral(data: { return { success: false, reason: "already_referred" }; } - // Credit both users. If a credit fails after the claim succeeds, retry the - // credit step alone — the unique claim already prevents a duplicate credit. + // Credit both users. Each credit has its own idempotency key (unique in your + // credits table), so if one fails you can retry the credits and only the + // missing one is written. await Promise.all([ creditUser(referrer.id, { type: "referral_bonus", amount: 100, reason: "Friend joined via your invite", + idempotencyKey: `referral:${data.newUserId}:referrer`, }), creditUser(data.newUserId, { type: "signup_bonus", amount: 50, reason: "Welcome bonus for joining via invite", + idempotencyKey: `referral:${data.newUserId}:referee`, }), ]); @@ -224,7 +229,7 @@ const events = { ``` -If you use the canonical [Core Event Set](/mini-apps/growth/analytics#2-%C2%B7-core-event-set-6-lines-of-code), `invite_link_created`/`invite_link_clicked` map to `invite_sent`/`invite_accepted` there. Pick one taxonomy and use it consistently. +If you use the canonical [Core Event Set](/mini-apps/growth/analytics#2-%C2%B7-core-event-set-6-lines-of-code), `invite_link_created` maps to `invite_sent` and `signup_source_invite` maps to `invite_accepted`; `invite_link_clicked` has no equivalent there. Pick one taxonomy and use it consistently. ### Key Metrics Dashboard diff --git a/mini-apps/growth/notifications.mdx b/mini-apps/growth/notifications.mdx index fed90f6..365a46a 100644 --- a/mini-apps/growth/notifications.mdx +++ b/mini-apps/growth/notifications.mdx @@ -45,7 +45,7 @@ Thoughtful, behavior‑based notifications keep users engaged long after they cl ### 6 · Copy Cheatsheet -- **Limits**: title ≤ 30 characters, message ≤ 200 characters (API-enforced). +- **Limits**: title ≤ 30 characters (≤ 16 plus `${username}` if you use it), message ≤ 200 characters (API-enforced). - **Lead with benefit**: "Earn 50 coins" beats "Check the app". - **Curiosity**: "Something new awaits …". - **Concrete numbers**: "30 s left" > "Hurry up". diff --git a/mini-apps/migration/minikit-v2.mdx b/mini-apps/migration/minikit-v2.mdx index ae458cf..0350e43 100644 --- a/mini-apps/migration/minikit-v2.mdx +++ b/mini-apps/migration/minikit-v2.mdx @@ -13,6 +13,7 @@ MiniKit 2.x consolidates command handling around async `MiniKit` methods and rem - Response interface changed to `{ executedWith, data }` - Types and helpers moved to `@worldcoin/minikit-js/commands`, `@worldcoin/minikit-js/siwe`, and `@worldcoin/minikit-js/address-book` - `walletAuth` nonce validation is stricter and expects an alphanumeric nonce without hyphens +- `signTypedData` deprecated - `sendTransaction` now takes encoded calldata `transactions` and returns `userOpHash` - Permit2 switched from SignatureTransfer to AllowanceTransfer - Standard ERC-20 `approve()` calls are now allowed, and approval will be revoked after the transaction diff --git a/mini-apps/quick-start/installing.mdx b/mini-apps/quick-start/installing.mdx index a963d30..9115e4e 100644 --- a/mini-apps/quick-start/installing.mdx +++ b/mini-apps/quick-start/installing.mdx @@ -10,7 +10,7 @@ description: "Create a Mini App with the official template or install MiniKit-JS ## Quick Start The fastest way to get started is by using our template next-15 repository. -Run the following command and follow the instructions to create a new mini app. This installs dependencies with `npm`; if you prefer `pnpm`, see [Manual Installation](#manual-installation). +Run the following command and follow the instructions to create a new mini app. This installs dependencies with `npm`; to use `pnpm`, add `--no-install` and run `pnpm install` in the new directory. ```bash npx @worldcoin/create-mini-app@latest my-first-mini-app @@ -48,6 +48,10 @@ pnpm install @worldcoin/minikit-js ``` + +**TypeScript**: the package's type declarations reference `window.WorldApp` without declaring it, so type-checking fails with `"skipLibCheck": false`. Set `"skipLibCheck": true` in `tsconfig.json` (the Next.js default), or add the [`Window.WorldApp` declaration](/mini-apps/quick-start/init#raw-world-app-object). + + ## Usage 1. Wrap your app with `MiniKitProvider` in a client component. This initializes MiniKit and makes it available throughout your app. @@ -93,7 +97,7 @@ import { MiniKit } from "@worldcoin/minikit-js"; console.log(MiniKit.isInstalled()); ``` -3. Or, in a React component, read the same state reactively with the `useMiniKit()` hook, used by the official starter template: +In a React component, you can read this reactively with the `useMiniKit()` hook, as the official starter template does: ```tsx import { useMiniKit } from "@worldcoin/minikit-js/minikit-provider"; @@ -116,7 +120,7 @@ The [World Docs MCP](/model-context-protocol/world-docs) lets any coding assista ```bash claude mcp add --transport http --scope project world-docs https://docs.world.org/mcp ``` - `--scope project` writes a shareable `.mcp.json` to your project (recommended). Omit it to register the server only for you, in your global Claude config. + `--scope project` writes a shareable `.mcp.json` to your project (recommended). Without it, the server is added only for you in this project (stored in `~/.claude.json`); use `--scope user` to add it to all your projects. ```bash diff --git a/mini-apps/quick-start/responses.mdx b/mini-apps/quick-start/responses.mdx index ed1fcfa..1e8c123 100644 --- a/mini-apps/quick-start/responses.mdx +++ b/mini-apps/quick-start/responses.mdx @@ -21,7 +21,7 @@ import type { async function signInWithWallet() { const input = { - nonce: "random-nonce-123", + nonce: "exampleNonce123", } satisfies MiniKitWalletAuthOptions; try { diff --git a/mini-apps/sharing/sage-qa.mdx b/mini-apps/sharing/sage-qa.mdx index 735f22a..0188535 100644 --- a/mini-apps/sharing/sage-qa.mdx +++ b/mini-apps/sharing/sage-qa.mdx @@ -5,7 +5,7 @@ title: "Sage Support" --- -The [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) is unavailable (its Vercel deployment is disabled), so the steps below cannot be completed. This page is deprecated until the dashboard is restored. +The [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) is unavailable, so the steps below can't be completed. [Sage](https://worldcoin.org/ecosystem/app_5dee2f19cd6eef599eb6ab275a0a7523) is an AI chatbot that lets users ask questions and get answers. From 76df93fbb638e3d6a700b3be5235097558c4f8d9 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Wed, 23 Sep 2026 15:43:29 -0700 Subject: [PATCH 7/8] docs(mini-apps): make referral credits resumable on retry - claimReferral returns the stored referrer; a retry for the same referral re-runs the idempotent credits instead of stopping at already_referred, so a partial failure can complete --- mini-apps/growth/invites-viral.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx index 8e3055a..0535ef5 100644 --- a/mini-apps/growth/invites-viral.mdx +++ b/mini-apps/growth/invites-viral.mdx @@ -135,16 +135,16 @@ export async function processReferral(data: { } // Atomically claim the referral (e.g. INSERT ... ON CONFLICT DO NOTHING on - // a unique referred_user_id column) before crediting anything. If two - // requests race, only one INSERT succeeds. - const claimed = await claimReferral(data.newUserId, referrer.id); - if (!claimed) { + // a unique referred_user_id column), then read back the stored referrer. If + // two requests race, only one INSERT succeeds. + const claimedBy = await claimReferral(data.newUserId, referrer.id); + if (claimedBy !== referrer.id) { return { success: false, reason: "already_referred" }; } // Credit both users. Each credit has its own idempotency key (unique in your - // credits table), so if one fails you can retry the credits and only the - // missing one is written. + // credits table; treat a duplicate as success), so retrying processReferral + // after a partial failure writes only the missing credit. await Promise.all([ creditUser(referrer.id, { type: "referral_bonus", From da6e2b663b941c696104edf85031d9275b848f36 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Wed, 23 Sep 2026 15:45:32 -0700 Subject: [PATCH 8/8] chore: retrigger Mintlify deployment (preview revalidation failed on Mintlify's side)