diff --git a/cspell.json b/cspell.json index 27f1888..4b556e8 100644 --- a/cspell.json +++ b/cspell.json @@ -28,7 +28,36 @@ "libsodium", "Salsa", "Poly", - "Bedrock" + "Bedrock", + "Completionist", + "diarias", + "Disponibles", + "están", + "idkit", + "IERC", + "inlines", + "Lazo", + "listas", + "localised", + "Mateo", + "mistico", + "Optimising", + "prêtes", + "quotidiennes", + "Recompensas", + "Récompenses", + "Salut", + "Sauton", + "siwe", + "sont", + "spammy", + "tute", + "userop", + "viem", + "virality", + "wagmi", + "XVCJ", + "zrok" ], "ignorePaths": [ "node_modules", @@ -38,7 +67,11 @@ "build", ".vscode" ], - "ignoreRegExpList": ["/\\b[A-Z]{2,}\\b/g", "/\\b[a-f0-9]{6,}\\b/gi"], + "ignoreRegExpList": [ + "/%2F\\w+/gi", + "/\\b[A-Z]{2,}\\b/g", + "/\\b[a-f0-9]{6,}\\b/gi" + ], "allowCompoundWords": true, "dictionaries": [ "en_US", diff --git a/docs.json b/docs.json index 8cde8a5..d9b1055 100644 --- a/docs.json +++ b/docs.json @@ -23,7 +23,7 @@ "anchors": [ { "anchor": "Mini Apps", - "href": "https://docs.world.org/mini-apps", + "href": "/mini-apps", "icon": "/images/mini-apps-card.svg", "color": { "light": "#fff", @@ -63,6 +63,103 @@ } ] }, + { + "tab": "Mini Apps", + "groups": [ + { + "group": "Introduction", + "pages": ["mini-apps/index"] + }, + { + "group": "Integrate", + "pages": [ + "mini-apps/quick-start/installing", + "mini-apps/quick-start/init", + "mini-apps/quick-start/commands", + "mini-apps/quick-start/responses", + "mini-apps/quick-start/testing", + "mini-apps/quick-start/app-store", + { + "group": "Guidelines", + "pages": [ + "mini-apps/guidelines/app-guidelines", + "mini-apps/guidelines/design-guidelines", + "mini-apps/guidelines/policy", + "mini-apps/guidelines/features-and-guidelines" + ] + } + ] + }, + { + "group": "Commands", + "pages": [ + "mini-apps/commands/verify", + "mini-apps/commands/pay", + "mini-apps/commands/wallet-auth", + "mini-apps/commands/send-transaction", + "mini-apps/commands/chat", + "mini-apps/commands/sign-message", + "mini-apps/commands/sign-typed-data", + "mini-apps/commands/share-contacts", + "mini-apps/commands/request-permission", + "mini-apps/commands/get-permissions", + "mini-apps/commands/how-to-send-notifications", + "mini-apps/commands/send-haptic-feedback", + "mini-apps/commands/share", + "mini-apps/commands/attestation", + "mini-apps/commands/close-miniapp" + ] + }, + { + "group": "Technical Reference", + "pages": [ + "mini-apps/reference/address-book", + "mini-apps/reference/usernames", + "mini-apps/reference/status-page", + "mini-apps/reference/payment-methods", + "mini-apps/reference/microphone", + { + "group": "Quick Actions", + "pages": [ + "mini-apps/sharing/quick-actions", + "mini-apps/sharing/add-money-qa", + "mini-apps/sharing/swap-qa", + "mini-apps/sharing/earn-wld-qa", + "mini-apps/sharing/dna-qa", + "mini-apps/sharing/sage-qa" + ] + } + ] + }, + { + "group": "Migration", + "pages": [ + "mini-apps/migration/web-to-miniapp", + "mini-apps/migration/standalone-dapp", + "mini-apps/migration/minikit-v2" + ] + }, + { + "group": "Further Reading", + "pages": [ + "mini-apps/more/webview-spec", + "mini-apps/ecosystem/ecosystem", + "mini-apps/more/community-tools-perks", + "mini-apps/more/faq", + { + "group": "Growth Playbook", + "pages": [ + "mini-apps/growth/index", + "mini-apps/growth/invites-viral", + "mini-apps/growth/gamification", + "mini-apps/growth/notifications", + "mini-apps/growth/analytics" + ] + } + ] + } + ] + }, { "tab": "Orb", "pages": ["orb/index"] diff --git a/images/docs/docs-meta.png b/images/docs/docs-meta.png new file mode 100644 index 0000000..130be7c Binary files /dev/null and b/images/docs/docs-meta.png differ diff --git a/images/docs/mini-apps-meta.png b/images/docs/mini-apps-meta.png new file mode 100644 index 0000000..742c123 Binary files /dev/null and b/images/docs/mini-apps-meta.png differ diff --git a/images/docs/mini-apps/commands/apple-pay.mp4 b/images/docs/mini-apps/commands/apple-pay.mp4 new file mode 100644 index 0000000..9b9e4b0 Binary files /dev/null and b/images/docs/mini-apps/commands/apple-pay.mp4 differ diff --git a/images/docs/mini-apps/commands/chat.mp4 b/images/docs/mini-apps/commands/chat.mp4 new file mode 100644 index 0000000..316f52b Binary files /dev/null and b/images/docs/mini-apps/commands/chat.mp4 differ diff --git a/images/docs/mini-apps/commands/contacts-command.mp4 b/images/docs/mini-apps/commands/contacts-command.mp4 new file mode 100644 index 0000000..852291f Binary files /dev/null and b/images/docs/mini-apps/commands/contacts-command.mp4 differ diff --git a/images/docs/mini-apps/commands/developer-portal-notifications.png b/images/docs/mini-apps/commands/developer-portal-notifications.png new file mode 100644 index 0000000..297e03f Binary files /dev/null and b/images/docs/mini-apps/commands/developer-portal-notifications.png differ diff --git a/images/docs/mini-apps/commands/google-pay-demo.jpg b/images/docs/mini-apps/commands/google-pay-demo.jpg new file mode 100644 index 0000000..486275b Binary files /dev/null and b/images/docs/mini-apps/commands/google-pay-demo.jpg differ diff --git a/images/docs/mini-apps/commands/notifications-command.mp4 b/images/docs/mini-apps/commands/notifications-command.mp4 new file mode 100644 index 0000000..d6a898c Binary files /dev/null and b/images/docs/mini-apps/commands/notifications-command.mp4 differ diff --git a/images/docs/mini-apps/commands/notifications-dev-portal.png b/images/docs/mini-apps/commands/notifications-dev-portal.png new file mode 100644 index 0000000..2ca1568 Binary files /dev/null and b/images/docs/mini-apps/commands/notifications-dev-portal.png differ diff --git a/images/docs/mini-apps/commands/pay-updated.gif b/images/docs/mini-apps/commands/pay-updated.gif new file mode 100644 index 0000000..97002ea Binary files /dev/null and b/images/docs/mini-apps/commands/pay-updated.gif differ diff --git a/images/docs/mini-apps/commands/permit2-whitelist.png b/images/docs/mini-apps/commands/permit2-whitelist.png new file mode 100644 index 0000000..e0e8d62 Binary files /dev/null and b/images/docs/mini-apps/commands/permit2-whitelist.png differ diff --git a/images/docs/mini-apps/commands/profilecard.mp4 b/images/docs/mini-apps/commands/profilecard.mp4 new file mode 100644 index 0000000..4c11b83 Binary files /dev/null and b/images/docs/mini-apps/commands/profilecard.mp4 differ diff --git a/images/docs/mini-apps/commands/share.mp4 b/images/docs/mini-apps/commands/share.mp4 new file mode 100644 index 0000000..f30afec Binary files /dev/null and b/images/docs/mini-apps/commands/share.mp4 differ diff --git a/images/docs/mini-apps/commands/transaction.mp4 b/images/docs/mini-apps/commands/transaction.mp4 new file mode 100644 index 0000000..d1bd8b1 Binary files /dev/null and b/images/docs/mini-apps/commands/transaction.mp4 differ diff --git a/images/docs/mini-apps/commands/verify.mp4 b/images/docs/mini-apps/commands/verify.mp4 new file mode 100644 index 0000000..0a95ee7 Binary files /dev/null and b/images/docs/mini-apps/commands/verify.mp4 differ diff --git a/images/docs/mini-apps/commands/walletauth.mp4 b/images/docs/mini-apps/commands/walletauth.mp4 new file mode 100644 index 0000000..b195c86 Binary files /dev/null and b/images/docs/mini-apps/commands/walletauth.mp4 differ diff --git a/images/docs/mini-apps/commands/whitelist.png b/images/docs/mini-apps/commands/whitelist.png new file mode 100644 index 0000000..1e60f11 Binary files /dev/null and b/images/docs/mini-apps/commands/whitelist.png differ diff --git a/images/docs/mini-apps/guidelines/bad-compressed.mp4 b/images/docs/mini-apps/guidelines/bad-compressed.mp4 new file mode 100644 index 0000000..424e930 Binary files /dev/null and b/images/docs/mini-apps/guidelines/bad-compressed.mp4 differ diff --git a/images/docs/mini-apps/guidelines/base-unit.png b/images/docs/mini-apps/guidelines/base-unit.png new file mode 100644 index 0000000..0c10d68 Binary files /dev/null and b/images/docs/mini-apps/guidelines/base-unit.png differ diff --git a/images/docs/mini-apps/guidelines/bottom-space-android.png b/images/docs/mini-apps/guidelines/bottom-space-android.png new file mode 100644 index 0000000..00b408f Binary files /dev/null and b/images/docs/mini-apps/guidelines/bottom-space-android.png differ diff --git a/images/docs/mini-apps/guidelines/bottom-space-ios.png b/images/docs/mini-apps/guidelines/bottom-space-ios.png new file mode 100644 index 0000000..5af093e Binary files /dev/null and b/images/docs/mini-apps/guidelines/bottom-space-ios.png differ diff --git a/images/docs/mini-apps/guidelines/content-card-image.png b/images/docs/mini-apps/guidelines/content-card-image.png new file mode 100644 index 0000000..53db6d6 Binary files /dev/null and b/images/docs/mini-apps/guidelines/content-card-image.png differ diff --git a/images/docs/mini-apps/guidelines/drawers-android.png b/images/docs/mini-apps/guidelines/drawers-android.png new file mode 100644 index 0000000..5b39b66 Binary files /dev/null and b/images/docs/mini-apps/guidelines/drawers-android.png differ diff --git a/images/docs/mini-apps/guidelines/drawers-ios.png b/images/docs/mini-apps/guidelines/drawers-ios.png new file mode 100644 index 0000000..1ae3319 Binary files /dev/null and b/images/docs/mini-apps/guidelines/drawers-ios.png differ diff --git a/images/docs/mini-apps/guidelines/good-compressed.mp4 b/images/docs/mini-apps/guidelines/good-compressed.mp4 new file mode 100644 index 0000000..ab15a93 Binary files /dev/null and b/images/docs/mini-apps/guidelines/good-compressed.mp4 differ diff --git a/images/docs/mini-apps/guidelines/keyboard-android.png b/images/docs/mini-apps/guidelines/keyboard-android.png new file mode 100644 index 0000000..64519e1 Binary files /dev/null and b/images/docs/mini-apps/guidelines/keyboard-android.png differ diff --git a/images/docs/mini-apps/guidelines/keyboard-ios.png b/images/docs/mini-apps/guidelines/keyboard-ios.png new file mode 100644 index 0000000..26aa8fd Binary files /dev/null and b/images/docs/mini-apps/guidelines/keyboard-ios.png differ diff --git a/images/docs/mini-apps/guidelines/main-pages-1.png b/images/docs/mini-apps/guidelines/main-pages-1.png new file mode 100644 index 0000000..deb1b00 Binary files /dev/null and b/images/docs/mini-apps/guidelines/main-pages-1.png differ diff --git a/images/docs/mini-apps/guidelines/main-pages-2.png b/images/docs/mini-apps/guidelines/main-pages-2.png new file mode 100644 index 0000000..8e6b7bf Binary files /dev/null and b/images/docs/mini-apps/guidelines/main-pages-2.png differ diff --git a/images/docs/mini-apps/guidelines/main-pages-4.png b/images/docs/mini-apps/guidelines/main-pages-4.png new file mode 100644 index 0000000..8027e9a Binary files /dev/null and b/images/docs/mini-apps/guidelines/main-pages-4.png differ diff --git a/images/docs/mini-apps/guidelines/main-pages-5.png b/images/docs/mini-apps/guidelines/main-pages-5.png new file mode 100644 index 0000000..ec218ca Binary files /dev/null and b/images/docs/mini-apps/guidelines/main-pages-5.png differ diff --git a/images/docs/mini-apps/guidelines/navigation-bar.png b/images/docs/mini-apps/guidelines/navigation-bar.png new file mode 100644 index 0000000..f293c5b Binary files /dev/null and b/images/docs/mini-apps/guidelines/navigation-bar.png differ diff --git a/images/docs/mini-apps/guidelines/paddings.png b/images/docs/mini-apps/guidelines/paddings.png new file mode 100644 index 0000000..b0242ef Binary files /dev/null and b/images/docs/mini-apps/guidelines/paddings.png differ diff --git a/images/docs/mini-apps/guidelines/secondary-pages-1.png b/images/docs/mini-apps/guidelines/secondary-pages-1.png new file mode 100644 index 0000000..9cea350 Binary files /dev/null and b/images/docs/mini-apps/guidelines/secondary-pages-1.png differ diff --git a/images/docs/mini-apps/guidelines/secondary-pages-2.png b/images/docs/mini-apps/guidelines/secondary-pages-2.png new file mode 100644 index 0000000..1bdc5ee Binary files /dev/null and b/images/docs/mini-apps/guidelines/secondary-pages-2.png differ diff --git a/images/docs/mini-apps/guidelines/states.png b/images/docs/mini-apps/guidelines/states.png new file mode 100644 index 0000000..d4f7948 Binary files /dev/null and b/images/docs/mini-apps/guidelines/states.png differ diff --git a/images/docs/mini-apps/guidelines/tab-bar-android.png b/images/docs/mini-apps/guidelines/tab-bar-android.png new file mode 100644 index 0000000..523ce76 Binary files /dev/null and b/images/docs/mini-apps/guidelines/tab-bar-android.png differ diff --git a/images/docs/mini-apps/guidelines/tab-bar-ios.png b/images/docs/mini-apps/guidelines/tab-bar-ios.png new file mode 100644 index 0000000..66aa66e Binary files /dev/null and b/images/docs/mini-apps/guidelines/tab-bar-ios.png differ diff --git a/images/docs/mini-apps/guidelines/toast-android.png b/images/docs/mini-apps/guidelines/toast-android.png new file mode 100644 index 0000000..2418305 Binary files /dev/null and b/images/docs/mini-apps/guidelines/toast-android.png differ diff --git a/images/docs/mini-apps/guidelines/toast-ios.png b/images/docs/mini-apps/guidelines/toast-ios.png new file mode 100644 index 0000000..f798637 Binary files /dev/null and b/images/docs/mini-apps/guidelines/toast-ios.png differ diff --git a/images/docs/mini-apps/mini-apps.png b/images/docs/mini-apps/mini-apps.png new file mode 100644 index 0000000..21a5778 Binary files /dev/null and b/images/docs/mini-apps/mini-apps.png differ diff --git a/images/docs/mini-apps/more/Thirdweb-Logo.png b/images/docs/mini-apps/more/Thirdweb-Logo.png new file mode 100644 index 0000000..77e0a17 Binary files /dev/null and b/images/docs/mini-apps/more/Thirdweb-Logo.png differ diff --git a/images/docs/mini-apps/more/alchemy.png b/images/docs/mini-apps/more/alchemy.png new file mode 100644 index 0000000..a50a1a4 Binary files /dev/null and b/images/docs/mini-apps/more/alchemy.png differ diff --git a/images/docs/mini-apps/more/cloudflare.png b/images/docs/mini-apps/more/cloudflare.png new file mode 100644 index 0000000..754bd0f Binary files /dev/null and b/images/docs/mini-apps/more/cloudflare.png differ diff --git a/images/docs/mini-apps/more/featured.jpg b/images/docs/mini-apps/more/featured.jpg new file mode 100644 index 0000000..dbab9e3 Binary files /dev/null and b/images/docs/mini-apps/more/featured.jpg differ diff --git a/images/docs/mini-apps/more/lazo.png b/images/docs/mini-apps/more/lazo.png new file mode 100644 index 0000000..70962f7 Binary files /dev/null and b/images/docs/mini-apps/more/lazo.png differ diff --git a/images/docs/mini-apps/notifications/notification-badges.jpg b/images/docs/mini-apps/notifications/notification-badges.jpg new file mode 100644 index 0000000..676c1f5 Binary files /dev/null and b/images/docs/mini-apps/notifications/notification-badges.jpg differ diff --git a/images/docs/mini-apps/quick-actions/add-money-qa.png b/images/docs/mini-apps/quick-actions/add-money-qa.png new file mode 100644 index 0000000..8dd0d03 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/add-money-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/dna-qa-send.png b/images/docs/mini-apps/quick-actions/dna-qa-send.png new file mode 100644 index 0000000..efd2c1b Binary files /dev/null and b/images/docs/mini-apps/quick-actions/dna-qa-send.png differ diff --git a/images/docs/mini-apps/quick-actions/dna-qa-swap.png b/images/docs/mini-apps/quick-actions/dna-qa-swap.png new file mode 100644 index 0000000..97d4348 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/dna-qa-swap.png differ diff --git a/images/docs/mini-apps/quick-actions/earn-wld-qa.png b/images/docs/mini-apps/quick-actions/earn-wld-qa.png new file mode 100644 index 0000000..5337bd2 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/earn-wld-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/eggs-vault-qa.png b/images/docs/mini-apps/quick-actions/eggs-vault-qa.png new file mode 100644 index 0000000..8b2e18c Binary files /dev/null and b/images/docs/mini-apps/quick-actions/eggs-vault-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/holdstation-buy-qa.png b/images/docs/mini-apps/quick-actions/holdstation-buy-qa.png new file mode 100644 index 0000000..1b6ad34 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/holdstation-buy-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/onepay-qa.png b/images/docs/mini-apps/quick-actions/onepay-qa.png new file mode 100644 index 0000000..ef217b5 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/onepay-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/orbital-qa.png b/images/docs/mini-apps/quick-actions/orbital-qa.png new file mode 100644 index 0000000..4ef54f9 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/orbital-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/puf-qa.png b/images/docs/mini-apps/quick-actions/puf-qa.png new file mode 100644 index 0000000..29de805 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/puf-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/sage1-qa.png b/images/docs/mini-apps/quick-actions/sage1-qa.png new file mode 100644 index 0000000..13b9600 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/sage1-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/sage2-qa.png b/images/docs/mini-apps/quick-actions/sage2-qa.png new file mode 100644 index 0000000..b4a97e2 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/sage2-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/swap-qa.png b/images/docs/mini-apps/quick-actions/swap-qa.png new file mode 100644 index 0000000..b80db95 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/swap-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/uno-qa.png b/images/docs/mini-apps/quick-actions/uno-qa.png new file mode 100644 index 0000000..6e64c36 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/uno-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/vuni-qa.png b/images/docs/mini-apps/quick-actions/vuni-qa.png new file mode 100644 index 0000000..ab2e255 Binary files /dev/null and b/images/docs/mini-apps/quick-actions/vuni-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/wcf-qa.png b/images/docs/mini-apps/quick-actions/wcf-qa.png new file mode 100644 index 0000000..fbfb2ab Binary files /dev/null and b/images/docs/mini-apps/quick-actions/wcf-qa.png differ diff --git a/images/docs/mini-apps/quick-actions/world-chat-qa.png b/images/docs/mini-apps/quick-actions/world-chat-qa.png new file mode 100644 index 0000000..00edf1f Binary files /dev/null and b/images/docs/mini-apps/quick-actions/world-chat-qa.png differ diff --git a/images/docs/mini-apps/quick-start/mini-app-status.png b/images/docs/mini-apps/quick-start/mini-app-status.png new file mode 100644 index 0000000..e5445eb Binary files /dev/null and b/images/docs/mini-apps/quick-start/mini-app-status.png differ diff --git a/images/docs/mini-apps/quick-start/mini-apps-stats.png b/images/docs/mini-apps/quick-start/mini-apps-stats.png new file mode 100644 index 0000000..d8aa4df Binary files /dev/null and b/images/docs/mini-apps/quick-start/mini-apps-stats.png differ diff --git a/images/docs/mini-apps/quick-start/submit-app.png b/images/docs/mini-apps/quick-start/submit-app.png new file mode 100644 index 0000000..2d9b9d3 Binary files /dev/null and b/images/docs/mini-apps/quick-start/submit-app.png differ diff --git a/images/docs/mini-apps/quick-start/template.png b/images/docs/mini-apps/quick-start/template.png new file mode 100644 index 0000000..8ab4b0f Binary files /dev/null and b/images/docs/mini-apps/quick-start/template.png differ diff --git a/images/docs/mini-apps/quick-start/test-mode.jpg b/images/docs/mini-apps/quick-start/test-mode.jpg new file mode 100644 index 0000000..3956e88 Binary files /dev/null and b/images/docs/mini-apps/quick-start/test-mode.jpg differ diff --git a/images/docs/mini-apps/virality/widget.jpg b/images/docs/mini-apps/virality/widget.jpg new file mode 100644 index 0000000..dc915cb Binary files /dev/null and b/images/docs/mini-apps/virality/widget.jpg differ diff --git a/index.mdx b/index.mdx index 35143b8..9a8a471 100644 --- a/index.mdx +++ b/index.mdx @@ -15,7 +15,7 @@ Tools for Humanity is a technology company building for humans in the age of AI.
World App provides simple access to the World Network. World Wallet, World Chat, World ID.
- +
Mini Apps
diff --git a/mini-apps/commands/attestation.mdx b/mini-apps/commands/attestation.mdx new file mode 100644 index 0000000..db0f17b --- /dev/null +++ b/mini-apps/commands/attestation.mdx @@ -0,0 +1,87 @@ +--- +title: "Attestation" +description: "Request an attestation token using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.attestation()` to request an app attestation token for a request hash. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniAppAttestationSuccessPayload, + MiniKitAttestationOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function requestAttestation() { + const input = { + requestHash: "0x1234", + } satisfies MiniKitAttestationOptions; + + const result: CommandResultByVia< + MiniAppAttestationSuccessPayload, + MiniAppAttestationSuccessPayload, + "minikit" + > = await MiniKit.attestation(input); + + console.log(result.data.token); +} +``` + +```ts title="Type" +type MiniKitAttestationOptions = { + requestHash: string; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type AttestationResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + token: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.mock.payload" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `unauthorized` | The request is not authorized | +| `attestation_failed` | Attestation generation failed | +| `integrity_failed` | Integrity checks failed | +| `invalid_input` | The request hash is invalid | +| `unsupported_version` | The command version is not supported | diff --git a/mini-apps/commands/chat.mdx b/mini-apps/commands/chat.mdx new file mode 100644 index 0000000..ec436c0 --- /dev/null +++ b/mini-apps/commands/chat.mdx @@ -0,0 +1,101 @@ +--- +title: "World Chat" +description: "Share a message through World Chat using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.chat()` to open World Chat with a prefilled message. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniAppChatSuccessPayload, + MiniKitChatOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function shareToChat() { + const input = { + message: "Check out this mini app", + to: ["andy"], + } satisfies MiniKitChatOptions; + + const result: CommandResultByVia< + MiniAppChatSuccessPayload, + MiniAppChatSuccessPayload, + "minikit" + > = await MiniKit.chat(input); + + console.log(result.data.count); +} +``` + +```ts title="Type" +type MiniKitChatOptions = { + message: string; + to?: string[]; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type ChatResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + count: number; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "count": 2, + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `user_rejected` | The user rejected the request | +| `send_failed` | Sending the message failed | +| `generic_error` | Unexpected failure | + +## Preview + +
+ +
diff --git a/mini-apps/commands/close-miniapp.mdx b/mini-apps/commands/close-miniapp.mdx new file mode 100644 index 0000000..53cfe96 --- /dev/null +++ b/mini-apps/commands/close-miniapp.mdx @@ -0,0 +1,72 @@ +--- +title: "Close Mini App" +description: "Programmatically close the mini app using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.closeMiniApp()` to programmatically close the mini app. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + CloseMiniAppResult, + MiniKitCloseMiniAppOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function closeMiniApp() { + const input = {} satisfies MiniKitCloseMiniAppOptions; + + const result: CommandResultByVia< + CloseMiniAppResult, + CloseMiniAppResult, + "minikit" + > = await MiniKit.closeMiniApp(input); + + return result.data.status; +} +``` + +```ts title="Type" +type MiniKitCloseMiniAppOptions = { + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type CloseMiniAppResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1 + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. diff --git a/mini-apps/commands/get-permissions.mdx b/mini-apps/commands/get-permissions.mdx new file mode 100644 index 0000000..c37f623 --- /dev/null +++ b/mini-apps/commands/get-permissions.mdx @@ -0,0 +1,90 @@ +--- +title: "Get Permissions" +description: "Read current mini app permission settings using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.getPermissions()` to read the current permission state for the mini app. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniAppGetPermissionsSuccessPayload, + MiniKitGetPermissionsOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function getPermissions() { + const input = {} satisfies MiniKitGetPermissionsOptions; + + const result: CommandResultByVia< + MiniAppGetPermissionsSuccessPayload, + MiniAppGetPermissionsSuccessPayload, + "minikit" + > = await MiniKit.getPermissions(input); + + console.log(result.data.permissions); +} +``` + +```ts title="Type" +type MiniKitGetPermissionsOptions = { + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type GetPermissionsResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + permissions: { + notifications?: any; + contacts?: any; + microphone?: any; + }; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "permissions": { + "notifications": true, + "contacts": false, + "microphone": true + }, + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `generic_error` | Unexpected failure | diff --git a/mini-apps/commands/how-to-send-notifications.mdx b/mini-apps/commands/how-to-send-notifications.mdx new file mode 100644 index 0000000..c0a5052 --- /dev/null +++ b/mini-apps/commands/how-to-send-notifications.mdx @@ -0,0 +1,95 @@ +--- +title: "Send Notifications" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Please take a minute to read the [Features & Guidelines](/mini-apps/guidelines/features-and-guidelines). +It's important to follow these, otherwise we may disable your ability to send notifications. + +To send notifications to users you need to: + +- Request permission in the Developer Portal Advanced settings, for your mini app, +- Request permission to send notifications from the user, via MiniKit (see [Request Permission](/mini-apps/commands/request-permission)), +- Actually send the notification using our API or the Developer Portal. + +## Calling the send-notification endpoint + +The API endpoint provides capabilities for sending notifications programmatically. These notifications can be localized, +which ensures you reach users in their preferred language. This results in a dramatically higher engagement rate. + +### Localization made simple + +```javascript +const response = await fetch( + "https://developer.worldcoin.org/api/v2/minikit/send-notification", + { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + app_id: "your_app_id", + wallet_addresses: ["0x123...", "0x456..."], + localisations: [ + { + language: "en", + title: "🎉 Rewards Available", + message: "Hey ${username}, your daily rewards are ready!", + }, + { + language: "es", + title: "🎉 Recompensas Disponibles", + message: "Hola ${username}, tus recompensas diarias están listas!", + }, + { + language: "fr", + title: "🎉 Récompenses Disponibles", + message: + "Salut ${username}, vos récompenses quotidiennes sont prêtes!", + }, + ], + mini_app_path: "worldapp://mini-app?app_id=your_app_id&path=/rewards", + }), + } +); +``` + +Each user automatically receives the notification in their preferred language. +If their language isn't included in your localizations, you'll receive a specific `reason` in the response. + +For complete API documentation including all supported languages and response formats, see the [API Reference](https://docs.world.org/api-reference/developer-portal/send-notification). + +## Manually sending notifications from Developer Portal + + + This form doesn't support localized notifications yet. To take advantage of + localizations, use the API. + + +You can send notifications to multiple wallet addresses (up to 1000) directly from the Developer Portal. + +
+ Notification Interface in the Developer Portal +
+ +Use the form to input addresses, and content details. Once you click the `Send` button and get a success response, your notifications will be queued for delivery. + +## Testing + +We currently have a limit of 40 notifications per 4 hours for unverified apps. This is to help you test your notification implementations. +Currently you will need to create a new app if your app is verified as it will default to the verified app's metadata. In addition, you need +to enable notifications for your mini app inside of World App to receive them. + +## Useful links + +- [Features & Guidelines](/mini-apps/guidelines/features-and-guidelines) +- [How To Request Notification Permissions](/mini-apps/commands/request-permission) +- [How To Get Notification Permissions](/mini-apps/commands/get-permissions) +- [Send Notification API Reference](https://docs.world.org/api-reference/developer-portal/send-notification) diff --git a/mini-apps/commands/pay.mdx b/mini-apps/commands/pay.mdx new file mode 100644 index 0000000..bf7719a --- /dev/null +++ b/mini-apps/commands/pay.mdx @@ -0,0 +1,184 @@ +--- +title: "Pay" +description: "Request a payment from the user" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +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. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import { + Tokens, + tokenToDecimals, + type CommandResultByVia, + type MiniKitPayOptions, + type PayResult, +} from "@worldcoin/minikit-js/commands"; + +export async function sendPayment() { + // Create a nonce in the backend to use as a reference for this payment. + const response = await fetch("/api/generate-nonce", { method: "POST" }); + const { id } = await response.json(); + + const input = { + reference: id, + to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + tokens: [ + { + symbol: Tokens.WLD, + token_amount: tokenToDecimals(1, Tokens.WLD).toString(), + }, + ], + description: "Example payment", + fallback: () => { + alert("Please complete the payment in World App to proceed."); + }, + }; + + const result: CommandResultByVia = + await MiniKit.pay(input); + + if (result.executedWith !== "minikit") { + return; + } + + const response = await fetch("/api/confirm-payment", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ payload: result.data }), + }); + + const confirmation = await response.json(); + if (!response.ok || !confirmation.verified) { + throw new Error("Payment could not be verified"); + } +} +``` + +```ts title="Type" +type MiniKitPayOptions = { + reference: string; + to: string; + tokens: { + symbol: Tokens; + token_amount: string; + }[]; + description: string; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type PayResponse = + | { + executedWith: "minikit"; + data: { + transactionId: string; + reference: string; + from: string; + chain: "worldchain"; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "transactionId": "tx_1234567890abcdef", + "reference": "order-123", + "from": "0x1234567890123456789012345678901234567890", + "chain": "worldchain", + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Backend Verification + +Always verify the payment on your backend before treating it as final. + +```ts +import { NextRequest, NextResponse } from "next/server"; +import type { PayResult } from "@worldcoin/minikit-js/commands"; + +type RequestBody = { + payload: PayResult; +}; + +export async function POST(req: NextRequest) { + const { payload } = (await req.json()) as RequestBody; + const expected = await getPendingPayment(payload.reference); + + if (!expected) { + return NextResponse.json({ verified: false }, { status: 404 }); + } + + const response = await fetch( + `https://developer.worldcoin.org/api/v2/minikit/transaction/${payload.transactionId}?app_id=${process.env.APP_ID}&type=payment`, + { + method: "GET", + headers: { + Authorization: `Bearer ${process.env.DEV_PORTAL_API_KEY}`, + }, + }, + ); + + if (!response.ok) { + return NextResponse.json({ verified: false }, { status: 502 }); + } + + const transaction = await response.json(); + const verified = + transaction.transaction_status === "mined" && + transaction.reference === expected.reference && + transaction.to.toLowerCase() === expected.to.toLowerCase() && + transaction.token === expected.token && + transaction.token_amount === expected.tokenAmount; + + return NextResponse.json({ verified }); +} +``` + +`getPendingPayment` must load the expected recipient, token, and amount from +your server-side order data. Never accept those values from the client. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `input_error` | The payment payload is invalid | +| `user_rejected` | The user rejected the request | +| `payment_rejected` | The user cancelled the payment | +| `invalid_receiver` | The recipient address is invalid or not allowed | +| `insufficient_balance` | The user does not have enough balance | +| `transaction_failed` | The payment failed on-chain | +| `generic_error` | Unexpected failure | +| `user_blocked` | Payments are not available in the user's region | + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Preview + +
+ Pay command demo +
diff --git a/mini-apps/commands/request-permission.mdx b/mini-apps/commands/request-permission.mdx new file mode 100644 index 0000000..0cc0f8a --- /dev/null +++ b/mini-apps/commands/request-permission.mdx @@ -0,0 +1,103 @@ +--- +title: "Request Permission" +description: "Request a mini app permission using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.requestPermission()` to ask the user for a permission such as notifications or microphone access. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import { + Permission, + type CommandResultByVia, + type MiniAppRequestPermissionSuccessPayload, + type MiniKitRequestPermissionOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function requestNotifications() { + const input = { + permission: Permission.Notifications, + } satisfies MiniKitRequestPermissionOptions; + + const result: CommandResultByVia< + MiniAppRequestPermissionSuccessPayload, + MiniAppRequestPermissionSuccessPayload, + "minikit" + > = await MiniKit.requestPermission(input); + + console.log(result.data.permission); +} +``` + +```ts title="Type" +type MiniKitRequestPermissionOptions = { + permission: Permission; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type RequestPermissionResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + permission: "notifications" | "contacts" | "microphone"; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "permission": "notifications", + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `user_rejected` | The user rejected the request | +| `generic_error` | Unexpected failure | +| `already_requested` | The permission prompt was already shown and rejected | +| `permission_disabled` | The permission is disabled | +| `already_granted` | The permission is already granted | +| `unsupported_permission` | The permission is not supported | + +## Preview + +
+ +
diff --git a/mini-apps/commands/send-haptic-feedback.mdx b/mini-apps/commands/send-haptic-feedback.mdx new file mode 100644 index 0000000..8bc144f --- /dev/null +++ b/mini-apps/commands/send-haptic-feedback.mdx @@ -0,0 +1,101 @@ +--- +title: "Send Haptic Feedback" +description: "Trigger haptic feedback using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.sendHapticFeedback()` to trigger native haptic feedback. + +## Haptic Types + +- **`"notification"`** — style: `"success"`, `"warning"`, or `"error"` +- **`"impact"`** — style: `"light"`, `"medium"`, or `"heavy"` +- **`"selection-changed"`** — no style needed + +## Basic Usage + + +```tsx title="Impact" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { MiniKitSendHapticFeedbackOptions } from "@worldcoin/minikit-js/commands"; + +export async function sendImpactHaptic() { + await MiniKit.sendHapticFeedback({ + hapticsType: "impact", + style: "medium", + } satisfies MiniKitSendHapticFeedbackOptions); +} +``` + +```tsx title="Selection Changed" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { MiniKitSendHapticFeedbackOptions } from "@worldcoin/minikit-js/commands"; + +export async function sendSelectionHaptic() { + await MiniKit.sendHapticFeedback({ + hapticsType: "selection-changed", + } satisfies MiniKitSendHapticFeedbackOptions); +} +``` + +```ts title="Type" +type MiniKitSendHapticFeedbackOptions = + | { + hapticsType: "notification"; + style: "error" | "success" | "warning"; + fallback?: () => unknown; + } + | { + hapticsType: "impact"; + style: "light" | "medium" | "heavy"; + fallback?: () => unknown; + } + | { + hapticsType: "selection-changed"; + fallback?: () => unknown; + }; +``` + + +## Result + + +```ts title="Type" +type SendHapticFeedbackResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `generic_error` | Unexpected failure | +| `user_rejected` | The request was rejected | diff --git a/mini-apps/commands/send-transaction.mdx b/mini-apps/commands/send-transaction.mdx new file mode 100644 index 0000000..baf8dec --- /dev/null +++ b/mini-apps/commands/send-transaction.mdx @@ -0,0 +1,277 @@ +--- +title: "Send Transaction" +description: "Send one or more World Chain transactions using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +**Breaking Changes in MiniKit v2**: +- SignatureTransfer is no longer supported. Please use Allowance Transfers documented below. +- Standard ERC-20 `approve()` calls now work in mini apps. Approval will be automatically revoked after the transaction. + +## Permit2 Allowance Transfers (Recommended) + +[Allowance transfers](https://docs.uniswap.org/contracts/permit2/reference/allowance-transfer#approve) are the recommended method for moving tokens in mini apps. World App automatically approves tokens to Permit2, so you can bundle the Permit2 approval and your contract call in a single `sendTransaction` for a better UX. +Expiration should always be set to 0 as the approval will be consumed in the same transaction. + + +Standard ERC-20 `approve()` also works if you prefer. + +```tsx title="Frontend" +import { MiniKit } from "@worldcoin/minikit-js"; +import { encodeFunctionData, parseEther } from "viem"; + +const PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; + +async function approveAndSwap( + token: `0x${string}`, + spender: `0x${string}`, + amount: bigint, +) { + const result = await MiniKit.sendTransaction({ + chainId: 480, + transactions: [ + // 1. Approve spender via Permit2 + { + to: PERMIT2, + data: encodeFunctionData({ + abi: [ + { + name: "approve", // You must use this method of Allowance Transfers + type: "function", + inputs: [ + { name: "token", type: "address" }, + { name: "spender", type: "address" }, + { name: "amount", type: "uint160" }, + { name: "expiration", type: "uint48" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + ], + functionName: "approve", + args: [ + token, + spender, + amount, + // Always set deadline to 0 as it will be consumed in the same transaction + 0, + ], + }), + }, + // 2. Call your contract (which pulls tokens via permit2.transferFrom) + { + to: spender, + data: encodeFunctionData({ + abi: [ + { + name: "swap", + type: "function", + inputs: [{ name: "amount", type: "uint256" }], + outputs: [], + stateMutability: "nonpayable", + }, + ], + functionName: "swap", + args: [amount], + }), + }, + ], + }); + + console.log(result.data.userOpHash); +} +``` + +```solidity title="Contract" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; + +contract Swap { + using SafeERC20 for IERC20; + + IERC20 public tokenA; + IERC20 public tokenB; + IAllowanceTransfer public immutable permit2; + + constructor(address _tokenA, address _tokenB, address _permit2) { + tokenA = IERC20(_tokenA); + tokenB = IERC20(_tokenB); + permit2 = IAllowanceTransfer(_permit2); + } + + function swap(uint256 amount) external { + // Pull tokenA from caller via Permit2 AllowanceTransfer + permit2.transferFrom( + msg.sender, + address(this), + uint160(amount), + address(tokenA) + ); + // Send tokenB to caller + tokenB.safeTransfer(msg.sender, amount); + } +} +``` + + + + World App automatically approves new ERC-20 tokens to the Permit2 contract. + Your contract only needs to call `permit2.transferFrom` — the token-level + approval is already in place. + + +## Result + + +```ts title="Type" +type SendTransactionResponse = + | { + executedWith: "minikit" | "wagmi"; + data: { + userOpHash: string; + status: "success"; + version: number; + from: string; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "userOpHash": "0x8004b63530b968a2a2c9ff414e01fc06a3ec5e4068d36d923df6aa4334744369", + "status": "success", + "version": 2, + "from": "0x1234567890123456789012345678901234567890", + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + + + If you are integrating through Wagmi, viem, or ethers with the World App + EIP-1193 provider, the standard `eth_sendTransaction` call resolves to the + MiniKit `userOpHash`, not a canonical transaction hash. Do not treat the + returned value as a final transaction hash or pass it directly to + `waitForTransactionReceipt`. If you need the final on-chain transaction hash, + resolve the user operation status first. + + +## Confirming the User Operation + +The command resolves as soon as the user operation is +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`. + + +```tsx title="React" +import { useUserOperationReceipt } from "@worldcoin/minikit-react"; +import { createPublicClient, http } from "viem"; +import { worldchain } from "viem/chains"; + +const client = createPublicClient({ + chain: worldchain, + transport: http("https://worldchain-mainnet.g.alchemy.com/public"), +}); + +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 +}; +``` + +```ts title="API" +type UserOperationStatusSuccess = { + status: "success"; + userOpHash: string; + sender: string; + transaction_hash: string; + nonce: string; +}; + +const response = await fetch( + `https://developer.world.org/api/v2/minikit/userop/${userOpHash}`, +); +const status = await response.json(); + +if (status.status === "success") { + const success = status as UserOperationStatusSuccess; + console.log(success.transaction_hash); +} +``` + + +See [GET /api/v2/minikit/userop/{user_op_hash}](https://docs.world.org/api-reference/developer-portal/get-user-operation) +for the full endpoint response shape. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `invalid_operation` | The requested operation is not allowed | +| `user_rejected` | The user rejected the request | +| `input_error` | The payload is invalid | +| `simulation_failed` | Simulation failed before execution | +| `transaction_failed` | The transaction failed after submission | +| `generic_error` | Unexpected failure | +| `disallowed_operation` | The requested operation is disallowed | +| `validation_error` | Validation failed before execution | +| `invalid_contract` | The contract is not allowed for your app | +| `malicious_operation` | The operation was flagged as malicious | +| `daily_tx_limit_reached` | Daily transaction limit reached | +| `permitted_amount_exceeds_slippage` | Permit2 amount exceeds allowed slippage | +| `permitted_amount_not_found` | Permit2 amount could not be resolved | + +## Fallback Behavior + +By default we intend for mini apps to be useable outside of World App. Fallbacks generally will not be needed for this command and you should instead follow the [migration path outlined](/mini-apps/migration/minikit-v2). + +## Allowlisting Contracts and Tokens + +Before your mini app can send transactions, you must allowlist the contracts and tokens it interacts with. Navigate to **Developer Portal > Mini App > Permissions** and add: + +- **Permit2 Tokens** — every ERC-20 token your app transfers via Permit2 +- **Contract Entrypoints** — every contract your app calls directly + +
+ Developer Portal showing Permit2 token and contract entrypoint whitelisting +

Developer Portal Whitelist

+
+ + + Transactions that touch non-whitelisted contracts or tokens will be blocked by + the backend with an `invalid_contract` error. + + +## Preview + +
+ +
diff --git a/mini-apps/commands/share-contacts.mdx b/mini-apps/commands/share-contacts.mdx new file mode 100644 index 0000000..228d669 --- /dev/null +++ b/mini-apps/commands/share-contacts.mdx @@ -0,0 +1,106 @@ +--- +title: "Share Contacts" +description: "Open the native contact picker using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.shareContacts()` to open the World App contact picker. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniKitShareContactsOptions, + ShareContactsResult, +} from "@worldcoin/minikit-js/commands"; + +export async function pickContacts() { + const input = { + isMultiSelectEnabled: true, + inviteMessage: "Join me in this mini app", + } satisfies MiniKitShareContactsOptions; + + const result: CommandResultByVia< + ShareContactsResult, + ShareContactsResult, + "minikit" + > = await MiniKit.shareContacts(input); + + console.log(result.data.contacts); +} +``` + +```ts title="Type" +type MiniKitShareContactsOptions = { + isMultiSelectEnabled?: boolean; + inviteMessage?: string; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type ShareContactsResponse = + | { + executedWith: "minikit"; + data: { + contacts: Array<{ + username: string; + walletAddress: string; + profilePictureUrl: string | null; + }>; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "contacts": [ + { + "username": "alex", + "walletAddress": "0x1234567890123456789012345678901234567890", + "profilePictureUrl": "https://cdn.example.com/profile/alex.png" + } + ], + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `user_rejected` | The user rejected the request | +| `generic_error` | Unexpected failure | + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Preview + +
+ +
diff --git a/mini-apps/commands/share.mdx b/mini-apps/commands/share.mdx new file mode 100644 index 0000000..a280bbe --- /dev/null +++ b/mini-apps/commands/share.mdx @@ -0,0 +1,104 @@ +--- +title: "Share" +description: "Open the native share sheet using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.share()` to open the native share sheet with text, links, or files. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniAppShareSuccessPayload, + MiniKitShareOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function shareLink() { + const input = { + title: "Invite Link", + text: "Use this invite code to join my mini app", + url: "https://world.org", + } satisfies MiniKitShareOptions; + + const result: CommandResultByVia< + MiniAppShareSuccessPayload, + MiniAppShareSuccessPayload, + "minikit" + > = await MiniKit.share(input); + + console.log(result.data.shared_files_count); +} +``` + +```ts title="Type" +type MiniKitShareOptions = { + files?: File[]; + title?: string; + text?: string; + url?: string; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type ShareResponse = + | { + executedWith: "minikit"; + data: { + status: "success"; + version: number; + shared_files_count: number; + timestamp: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "shared_files_count": 2, + "timestamp": "2026-03-28T18:24:00.000Z" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `user_rejected` | The user rejected the share flow | +| `generic_error` | Unexpected failure | +| `invalid_file_name` | One or more file names are invalid | + +## Preview + +
+ +
diff --git a/mini-apps/commands/sign-message.mdx b/mini-apps/commands/sign-message.mdx new file mode 100644 index 0000000..8072528 --- /dev/null +++ b/mini-apps/commands/sign-message.mdx @@ -0,0 +1,88 @@ +--- +title: "Sign Message" +description: "Sign an EIP-191 message using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.signMessage()` to request a personal signature from the user's wallet. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniAppSignMessageSuccessPayload, + MiniKitSignMessageOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function signMessage() { + const input = { + message: "Hello world", + } satisfies MiniKitSignMessageOptions; + + const result: CommandResultByVia = + await MiniKit.signMessage(input); + + console.log(result.data.signature); +} +``` + +```ts title="Type" +type MiniKitSignMessageOptions = { + message: string; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type SignMessageResponse = + | { + executedWith: "minikit" | "wagmi"; + data: { + status: "success"; + version: number; + signature: string; + address: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "signature": "0xabcdef1234567890", + "address": "0x1234567890123456789012345678901234567890" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Notes + +Verify signatures in a trusted environment before using them for sensitive application logic. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `invalid_message` | The message payload is invalid | +| `user_rejected` | The user rejected the request | +| `generic_error` | Unexpected failure | diff --git a/mini-apps/commands/sign-typed-data.mdx b/mini-apps/commands/sign-typed-data.mdx new file mode 100644 index 0000000..6a42163 --- /dev/null +++ b/mini-apps/commands/sign-typed-data.mdx @@ -0,0 +1,111 @@ +--- +title: "Sign Typed Data" +description: "Sign EIP-712 typed data using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.signTypedData()` to request an EIP-712 signature from the user's wallet. + +## Basic Usage + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniAppSignTypedDataSuccessPayload, + MiniKitSignTypedDataOptions, +} from "@worldcoin/minikit-js/commands"; + +export async function signTypedData() { + const input = { + primaryType: "Mail", + domain: { + name: "Example", + version: "1", + chainId: 480, + }, + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { name: "chainId", type: "uint256" }, + ], + Mail: [ + { name: "contents", type: "string" }, + ], + }, + message: { + contents: "Hello world", + }, + } satisfies MiniKitSignTypedDataOptions; + + const result: CommandResultByVia = + await MiniKit.signTypedData(input); + + console.log(result.data.signature); +} +``` + +```ts title="Type" +type MiniKitSignTypedDataOptions = { + types: TypedData; + primaryType: string; + message: Record; + domain?: TypedDataDomain; + chainId?: number; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type SignTypedDataResponse = + | { + executedWith: "minikit" | "wagmi"; + data: { + status: "success"; + version: number; + signature: string; + address: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "status": "success", + "version": 1, + "signature": "0xabcdef1234567890", + "address": "0x1234567890123456789012345678901234567890" + } +} +``` + + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `invalid_operation` | The request contains an invalid operation | +| `user_rejected` | The user rejected the request | +| `input_error` | The payload is invalid | +| `simulation_failed` | Simulation failed before signing | +| `generic_error` | Unexpected failure | +| `disallowed_operation` | The operation is not allowed | +| `invalid_contract` | The contract or domain is invalid | +| `malicious_operation` | The request was flagged as malicious | diff --git a/mini-apps/commands/verify.mdx b/mini-apps/commands/verify.mdx new file mode 100644 index 0000000..cfe1b38 --- /dev/null +++ b/mini-apps/commands/verify.mdx @@ -0,0 +1,10 @@ +--- +title: "World ID" +description: "World ID verification has moved to IDKit." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +World ID has been unified into [IDKit](https://docs.world.org/world-id/idkit/mini-apps), so the same integration works in both mini apps and desktop. + +If you are migrating from MiniKit 1.x, replace `MiniKit.verify(...)` or `MiniKit.commandsAsync.verify(...)` with IDKit. See [IDKit for Mini Apps](https://docs.world.org/world-id/idkit/mini-apps) for the Mini App integration shape, including Selfie Check. diff --git a/mini-apps/commands/wallet-auth.mdx b/mini-apps/commands/wallet-auth.mdx new file mode 100644 index 0000000..2f8c237 --- /dev/null +++ b/mini-apps/commands/wallet-auth.mdx @@ -0,0 +1,196 @@ +--- +title: "Wallet Authentication" +description: "Authenticate a user with Sign-In with Ethereum using the unified MiniKit API." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use `MiniKit.walletAuth()` to authenticate a user with Sign-In with Ethereum inside World App. + +This is the recommended authentication flow for mini apps. + +## Basic Usage + +Generate the nonce on your backend. The nonce must be alphanumeric and at least 8 characters. + + +```tsx title="Example" +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniKitWalletAuthOptions, + WalletAuthResult, +} from "@worldcoin/minikit-js/commands"; + +export async function signInWithWallet() { + const response = await fetch("/api/nonce"); + const { nonce } = await response.json(); + + const input = { + nonce, + statement: "Sign in to Example Mini App", + expirationTime: new Date(Date.now() + 1000 * 60 * 60), + // requestId: "optional-tracking-id", + // notBefore: new Date(), + } satisfies MiniKitWalletAuthOptions; + + const result: CommandResultByVia = + await MiniKit.walletAuth(input); + + if (result.executedWith === "fallback") { + return; + } + + await fetch("/api/complete-siwe", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + payload: result.data, + nonce, + }), + }); +} +``` + +```ts title="Type" +type MiniKitWalletAuthOptions = { + nonce: string; + statement?: string; + expirationTime?: Date; + notBefore?: Date; + requestId?: string; + fallback?: () => unknown; +}; +``` + + +## Result + + +```ts title="Type" +type WalletAuthResponse = + | { + executedWith: "minikit" | "wagmi"; + data: { + address: string; + message: string; + signature: string; + }; + } + | { + executedWith: "fallback"; + data: unknown; + }; +``` + +```json title="Example" +{ + "executedWith": "minikit", + "data": { + "address": "0x1234567890123456789012345678901234567890", + "message": "example.com wants you to sign in with your Ethereum account", + "signature": "0xabcdef1234567890" + } +} +``` + + +## Backend Verification + +Always verify the returned SIWE payload on your backend. + +```ts +import { cookies } from "next/headers"; +import { NextRequest, NextResponse } from "next/server"; +import type { MiniAppWalletAuthSuccessPayload } from "@worldcoin/minikit-js/commands"; +import { verifySiweMessage } from "@worldcoin/minikit-js/siwe"; + +type RequestBody = { + payload: MiniAppWalletAuthSuccessPayload; + nonce: string; +}; + +export async function POST(req: NextRequest) { + const { payload, nonce } = (await req.json()) as RequestBody; + const cookieStore = await cookies(); + + if (nonce !== cookieStore.get("siwe")?.value) { + return NextResponse.json( + { isValid: false, error: "Invalid nonce" }, + { status: 400 }, + ); + } + + try { + // Optional: pass statement and requestId for additional server-side validation + const verification = await verifySiweMessage( + payload, + nonce, + // statement, — validates the statement matches what you sent + // requestId, — validates the request ID matches what you sent + // viemClient, — custom viem Client; defaults to a public Worldchain client + ); + + if (!verification.isValid) { + return NextResponse.json({ isValid: false }, { status: 401 }); + } + + cookieStore.delete("siwe"); + return NextResponse.json({ + isValid: true, + address: verification.siweMessageData.address, + }); + } catch (error) { + return NextResponse.json( + { + isValid: false, + error: error instanceof Error ? error.message : "Unknown error", + }, + { status: 400 }, + ); + } +} +``` + +## Options + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `nonce` | `string` | Yes | Alphanumeric, at least 8 characters | +| `statement` | `string` | No | Human-readable statement included in the SIWE message | +| `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 | + +## Notes + +- Use `MiniKit.user.walletAddress` after successful auth if you need cached user state +- Use `MiniKit.getUserByAddress()` or `MiniKit.getUserByUsername()` to resolve username and profile metadata +- Do not use World ID verification as a login substitute + +## Error Codes + +| Code | Meaning | +| --- | --- | +| `malformed_request` | The SIWE request payload is invalid | +| `user_rejected` | The user rejected the signature request | +| `generic_error` | Unexpected failure | + +## Fallback Behavior + +Define a custom fallback in the command payload for support outside mini apps. + +## Preview + +
+ +
diff --git a/mini-apps/ecosystem/ecosystem.mdx b/mini-apps/ecosystem/ecosystem.mdx new file mode 100644 index 0000000..92b57b1 --- /dev/null +++ b/mini-apps/ecosystem/ecosystem.mdx @@ -0,0 +1,36 @@ +--- +title: "Ecosystem Tools" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Ecosystem tools and integrations for builders on World Chain and Mini Apps. + + + {/* + // Integrate token functionality into your dApps: token lists and balances, + // transaction history, send tokens, swaps, and prebuilt widgets (connect + // wallet, token selector, transaction modals). + */} + + Early builders get ~10,000 real users sent to your Mini App. Free analytics + and smart push notifications. By [Human Labs](https://x.com/WorldHumanLabs). + + + Generate Uniswap API keys to add swap and LP functionality directly to your + app in minutes. For agentic workflows, use the Uniswap Skill to integrate + via AI agents. + + diff --git a/mini-apps/growth/analytics.mdx b/mini-apps/growth/analytics.mdx new file mode 100644 index 0000000..1211717 --- /dev/null +++ b/mini-apps/growth/analytics.mdx @@ -0,0 +1,59 @@ +--- +title: Data & Analytics +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Track only what answers **critical product questions**—nothing more. +This page shows you **what to measure, why, and how to start in one sprint**. + +## 1 · Mental Model _Q → M → E_ + +> **Question → Metric → Event** + +1. **Question** you want answered +2. **Metric** that quantifies it +3. **Event** (or two) that feed that metric + +| Product Question | Metric (M) | Minimal Events (E) | +| ---------------------------- | ----------------------------- | -------------------------------- | +| Do users see value on day 0? | **Conversion to First Value** | `signup`, `first_value` | +| Do they come back? | **D1 / D7 Retention** | `app_open` | +| Will growth be organic? | **Invite Acceptance Rate** | `invite_sent`, `invite_accepted` | +| Can we re‑engage them? | **Push Open Rate** | `notification_open` | + +If an event doesn't roll up to a key question, skip it. + +## 2 · Core Event Set (6 Lines of Code) + +```ts +track('app_open') // every launch +track('signup', { method: 'id' }) +track('first_value', { action: '🎉' }) +track('invite_sent') +track('invite_accepted') +track('notification_open') +``` + +That is enough to build funnels, retention, and viral metrics. + +## 3 · Action Plan + +**Focus on these 4 metrics weekly until they're all green:** + +- **Signup → First Value %** (target: ≥40%) - Are users getting value immediately? +- **D1 Retention** (target: ≥25%) - Are they coming back the next day? +- **Invite Acceptance %** (target: ≥15%) - Is word-of-mouth working? +- **Push Open %** (target: ≥15%) - Can we re-engage users? + +**Next steps:** Pick the lowest metric and spend 1-2 sprints fixing it. Don't touch anything else until all four are hitting targets. + +## 4 · Common Pitfalls to Dodge + +- **Event sprawl** – >10 events = analysis paralysis. +- **Vanity charts** – page views ≠ value. +- **No cohorts** – averages hide churn. +- **Premature segmentation** – wait for 1k users. +- **Ignoring retention** – acquisition is useless without it. + +Analytics should light the path, not become the journey. Keep it lean, learn fast, and build what moves the needle. diff --git a/mini-apps/growth/gamification.mdx b/mini-apps/growth/gamification.mdx new file mode 100644 index 0000000..38fe753 --- /dev/null +++ b/mini-apps/growth/gamification.mdx @@ -0,0 +1,153 @@ +--- +title: Gamification +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Gamification isn't about turning your app into a game—it's about using proven psychological principles to make routine actions more engaging and rewarding. + +## Why It Works + +Game mechanics tap into fundamental human psychology: our need for achievement, progress, and social comparison. Duolingo saw retention jump from 12% D1 to 55% after implementing streak mechanics. The key is choosing mechanics that align with your core user behavior, not just adding badges everywhere. + +## Core Mechanics by Impact & Effort + +| Mechanic | Effort | Why It Works | Quick Implementation | +| ------------------- | ------ | ------------------------------------------------------- | ------------------------------------------------------------ | +| **Daily Streaks** | ★★☆☆☆ | Creates habit formation; users hate breaking chains | Track daily actions, show streak count, celebrate milestones | +| **Progress Bars** | ★☆☆☆☆ | Visual momentum; users hate seeing 80% incomplete | CSS progress indicators for any goal completion | +| **Badges & Levels** | ★★☆☆☆ | Collectibles trigger completion bias | Emoji icons + achievement unlocks | +| **Leaderboards** | ★★☆☆☆ | Social comparison drives engagement | Simple top 10 list, refresh weekly | +| **Mystery Rewards** | ★★☆☆☆ | Variable rewards release dopamine (slot machine effect) | Daily random reward from small pool | + +## Strategic Implementation + +### 1. Daily Streaks + +**Why this first**: Highest retention impact with moderate effort. Creates powerful daily habit loops. + +**Core concept**: Track consecutive days of user activity, celebrate milestones, make breaking streaks feel costly. + +**Implementation strategy**: + +- Store `currentStreak`, `longestStreak`, `lastActiveDate` per user +- Check daily: consecutive day = +1, gap = reset to 1 +- Celebrate at days 3, 7, 14, 30 with visual rewards +- Show streak prominently in app header + +```typescript +// Simple streak logic +const updateStreak = (lastActive: string, today: string, streak: number) => { + const daysSinceLastActive = getDaysDifference(lastActive, today) + if (daysSinceLastActive === 0) return streak + return daysSinceLastActive === 1 ? streak + 1 : 1 +} +``` + +### 2. Progress Bars & Visual Momentum + +Users hate leaving things 80% complete. Visual progress creates urgency to finish. + +**Where to use**: + +- **Profile completion:** 2/5 steps remaining +- **Daily/weekly goals:** 7/10 tasks done +- **Social milestones:** 3/5 friends invited +- **Skill progression**: Level 4: 80% to Level 5 + +**Key principle**: Always show progress toward the next achievable milestone, not distant end goals. + +### 3. Achievement Badges + +**Strategy**: Create collectible moments that trigger completion bias and provide social proof. + +**Badge categories that work**: + +- **Onboarding**: "First Steps" (welcome badge), "Explorer" (tried 3 features) +- **Social**: "Social Butterfly" (first share), "Connector" (5 friends invited) +- **Engagement**: "Week Warrior" (7-day streak), "Power User" (daily active for 30 days) +- **Milestones**: "High Achiever" (reached level 10), "Completionist" (100% profile) + +**Design tips**: + +- Make early badges easy to get within first session +- Create clear progression: Common → Rare → Epic +- Show badge collection in user profile for social proof + +### 4. Social Leaderboards + +**When to use**: Best for apps with clear scoring metrics (points, levels, achievements completed). + +**Types that work**: + +- **Weekly leaderboards**: Reset regularly so everyone has a chance +- **Friend leaderboards**: Compare with people you know (higher engagement) +- **Category leaderboards**: "Top Streaks", "Most Social", "Fastest Completion" + +**Key strategies**: + +- Keep it simple: Top 10 list with current user highlighted +- Refresh weekly to prevent permanent dominance +- Only show verified users to prevent gaming + +**World App advantage**: World ID ensures fair competition with one-person-one-account guarantee. + +### 5. Variable Reward Systems + +**Psychology**: Variable rewards trigger dopamine more than predictable ones (slot machine effect). + +**Implementation ideas**: + +- **Daily mystery box**: Random reward from a small pool (coins, badges, features) +- **Streak bonuses**: Random multiplier for milestone completions +- **Surprise rewards**: Occasional "lucky day" bonuses for regular actions +- **Loot boxes**: Earned through achievements, contain random useful items + +**Reward pool strategy**: + +- 70% common rewards (small coin amounts, basic items) +- 25% rare rewards (larger bonuses, temporary premium features) +- 5% epic rewards (exclusive badges, significant bonuses) + +**Key principle**: Make the anticipation of opening more exciting than the reward itself. + +## Design Principles + +### 1. One Core Loop + +Pick one primary habit loop and nail it before adding more. +**Example flow**: Daily check-in → earn streak → unlock reward → share achievement → invite friends + +### 2. Early Wins + +Users should earn their first badge/reward within 30 seconds of first use. This creates immediate positive reinforcement and sets expectations for future rewards. + +### 3. Surface Progress Everywhere + +- Show current streak in app header/navigation +- Display progress bars for any incomplete goals +- Badge count in user profile for social proof +- Preview next achievable reward/milestone + +### 4. Measure & Optimize + +Track key events: `streak_extended`, `badge_earned`, `leaderboard_viewed`, `reward_claimed` + +**Golden rule**: If a mechanic doesn't improve D7 retention after 2 weeks, remove it. + +## Common Pitfalls + +1. **Over-gamification**: Don't add badges for every tiny action—dilutes achievement value +2. **Participation trophies**: Make early badges easy but later ones meaningful +3. **Pay-to-win mechanics**: Keep purchases separate from core progression +4. **Feature creep**: Start with one mechanic, prove it works, then expand + +## Your Next Steps + +1. **Start simple**: Implement daily streaks first—highest impact for effort invested +2. **Add visual progress**: One progress bar or completion indicator +3. **Create 3-5 early badges**: Tied directly to your core user actions +4. **Measure ruthlessly**: Track D7 retention before/after each mechanic +5. **Expand gradually**: Only add new mechanics after current ones prove effective + +The goal isn't to build a game —it's to make your core experience more engaging and habit-forming. diff --git a/mini-apps/growth/index.mdx b/mini-apps/growth/index.mdx new file mode 100644 index 0000000..53948e1 --- /dev/null +++ b/mini-apps/growth/index.mdx @@ -0,0 +1,24 @@ +--- +title: Overview +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +A focused, step-by-step guide to growing your mini app within the World ecosystem. Everything is scoped to what an indie hacker or small team can realistically ship in a few weeks. + +## Overview & Philosophy + +Building a successful mini app requires more than great features—you need users to discover, engage with, and stick around. This growth playbook focuses on four key areas that drive sustainable growth: + +1. **[Invites & Viral Loops](/mini-apps/growth/invites-viral)** - Turn your users into advocates +2. **[Gamification](/mini-apps/growth/gamification)** - Build engagement through game mechanics +3. **[Retention via Notifications](/mini-apps/growth/notifications)** - Keep users coming back +4. **[Data & Analytics](/mini-apps/growth/analytics)** - Measure what matters + +## Real-World Results + +The strategies in this playbook are based on proven case studies: + +- **PayPal's referral program**: 7-10% daily growth +- **Dropbox's two-sided rewards**: 3900% growth in 15 months +- **Duolingo's streak mechanics**: Retention jumped from 12% to 55% \ No newline at end of file diff --git a/mini-apps/growth/invites-viral.mdx b/mini-apps/growth/invites-viral.mdx new file mode 100644 index 0000000..d4741e5 --- /dev/null +++ b/mini-apps/growth/invites-viral.mdx @@ -0,0 +1,250 @@ +--- +title: Invites & Viral Loops +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +## Why It Works + +Referral programs are proven growth drivers because they leverage trust. People are 4x more likely to try something recommended by a friend versus discovering it through ads. PayPal's $20/$20 referral program produced 7-10% daily growth, while Dropbox's free storage rewards drove 3900% growth in 15 months. + +## Step-by-Step Implementation + +### 1. Set Up Universal Links + +Create server-side invite pages that work across all platforms: + +```typescript +// pages/invite.tsx +import { useEffect } from "react"; +import { useRouter } from "next/router"; + +export default function InvitePage() { + const { code } = useRouter().query; + + useEffect(() => { + if (typeof code !== "string") return; + + // Redirect to mini app + const path = `/invite?${new URLSearchParams({ code })}`; + window.location.href = `https://world.org/mini-app?app_id=${YOUR_APP_ID}&path=${encodeURIComponent(path)}` + }, [code]) + + return
Redirecting to mini app...
+} +``` + +Universal-link format + +`https://world.org/mini-app?app_id={app_id}&path={path}` + +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. +### 2. Generate Share Links + +Create a shareable link that includes the referral information: + +```typescript +function generateInviteLink(userId: string): string { + const baseUrl = "https://world.org/mini-app"; + const appId = "your_app_id"; + const query = new URLSearchParams({ code: userId }); + const path = encodeURIComponent(`/invite?${query}`); + + return `${baseUrl}?app_id=${appId}&path=${path}`; +} +``` + +### 3. Implement Share Functionality + +Add share buttons at key moments in your user journey: + +```typescript +import { MiniKit } from "@worldcoin/minikit-js"; + +async function shareInvite() { + const inviteLink = generateInviteLink(currentUser.id); + + try { + await MiniKit.share({ + title: "Join me on [Your App Name]!", + text: `I'm using this amazing mini app. Join me and we both get rewards! 🎁`, + url: inviteLink, + }); + + // Track the share event + trackEvent("invite_link_created", { + user_id: currentUser.id, + share_method: "native", + }); + } catch (error) { + console.error("Share failed:", error); + } +} +``` + +### 4. Handle Incoming Referrals + +Process referral codes when new users sign up: + +```typescript +// On app initialization +function handleReferral() { + const urlParams = new URLSearchParams(window.location.search); + const refCode = urlParams.get("code"); + + if (refCode && !currentUser.referredBy) { + // Credit the referrer + fetch("/api/process-referral", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + referrerCode: refCode, + }), + }); + } +} +``` + +### 5. Reward System + +Implement two-sided rewards that benefit both parties: + +```typescript +// api/process-referral.ts +export async function processReferral( + authenticatedUserId: string, + data: { referrerCode: string }, +) { + const newUserId = authenticatedUserId; + const referrer = await getUserByCode(data.referrerCode); + if (!referrer) { + return { success: false, reason: "invalid_referrer" }; + } + + if (referrer.id === newUserId) { + return { success: false, reason: "self_referral" }; + } + + const claimedBy = await claimReferral(newUserId, referrer.id); + if (claimedBy !== referrer.id) { + return { success: false, reason: "already_referred" }; + } + + // Use unique idempotency keys so retries cannot issue duplicate credits. + await Promise.all([ + creditUser(referrer.id, { + type: "referral_bonus", + amount: 100, + reason: "Friend joined via your invite", + idempotencyKey: `referral:${newUserId}:referrer`, + }), + creditUser(newUserId, { + type: "signup_bonus", + amount: 50, + reason: "Welcome bonus for joining via invite", + idempotencyKey: `referral:${newUserId}:referee`, + }), + ]); + + return { success: true }; +} +``` + +### 6. Support Deferred Deep Links (Android) + +``` +https://play.google.com/store/apps/details?id=com.worldcoin&referrer=app_id%3D{app_id}%26path%3D{path} +``` + +World App prompts the install and then automatically forwards the user to your specified path. + +### Optimising Social-Share Previews + +World app universal links will forward the og:image from your integration url. + +Eg. `world.org/mini-app?app_id=1234&path=/invite?code=abcd` will forward the og:image from `[your-miniapp-url]/invite?code=abcd`. +### Inside World App Discovery + +- **Quick Actions**: Mini apps can hand off context to one another. Split distinct flows into focused apps that trigger each other with Quick Actions. + +- **Widget**: Your mini app can live on the phone's home screen. Prompt the user to add the mini app as a widget in the home screen. + +
+ Widget +
+Widget on iOS + +## Design Best Practices + +### Placement Strategy + +- **After first value delivery**: When user completes their first meaningful action +- **Post-achievement**: Right after earning a badge, completing a level, or winning +- **Onboarding finale**: As the last step of user setup + +### Copy That Converts + +- **Personal benefit first**: "Get 100 coins for each friend who joins" +- **Mutual benefit**: "You both get rewards when they sign up" +- **Social proof**: "Join 10,000+ users already earning rewards" + +### Visual Design + +- Use prominent, action-oriented buttons ("Invite Friends", "Share & Earn") +- Show potential rewards clearly with icons or progress bars +- Include preview of what the shared content looks like + +## Metrics to Track + +Monitor these key events to measure your viral loop performance: + +```typescript +// Essential tracking events +const events = { + invite_link_created: { user_id, share_method }, + invite_link_clicked: { ref_code, source }, + signup_source_invite: { ref_code, new_user_id }, + referral_reward_granted: { referrer_id, new_user_id, reward_amount }, +}; +``` + +### Key Metrics Dashboard + +- **Invite Conversion Rate**: (Signups from invites) / (Total invite links clicked) +- **K-Factor**: (New users from invites) / (Total active users) +- **Viral Cycle Time**: Average time from invite sent to new user activated +- **Reward Cost per Acquisition**: Total rewards paid / New users acquired + +## Quick A/B Test Ideas + +Test these variables to optimize your viral loop: + +1. **Reward Amount**: Test 50 vs 100 vs 200 coin rewards +2. **Timing**: Share prompt after first win vs after onboarding +3. **Copy**: Personal benefit vs mutual benefit messaging +4. **Incentive Type**: Coins vs premium features vs exclusive content + + + Start with small reward amounts and scale up based on unit economics. Cap + total lifetime rewards per user to control costs. + + +## Next Steps + +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 +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 new file mode 100644 index 0000000..2fc2763 --- /dev/null +++ b/mini-apps/growth/notifications.mdx @@ -0,0 +1,91 @@ +--- +title: Retention via Notifications +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Thoughtful, behavior‑based notifications keep users engaged long after they close your Mini App. Done right, they lift retention and even earn you a coveted **home‑screen badge** in World App. + +### 1 · Why They Matter + +| Benefit | Details | +| -------------------- | ------------------------------------------------------------------- | +| **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. | + +### 2 · Quality Thresholds + +| Open‑Rate (7‑day) | Platform Action | Your Next Step | +| ----------------- | ----------------- | --------------------------------- | +| **< 10%** | Paused for 1 week | Audit triggers & copy immediately | +| **10–14%** | Delivery active | Improve targeting and copy | +| **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. + +### 4 · Trigger Library + +| Trigger | Why It Works | Sample Title | +| ---------------- | --------------------- | --------------------------------- | +| **Achievement** | Users love quick wins | `🎉 Level 2 unlocked!` | +| **Streak Risk** | Loss aversion | `🔥 Keep your 5‑day streak alive` | +| **Social** | FOMO / competition | `👀 Maya just beat your score` | +| **Limited‑time** | Urgency | `⏰ Double rewards end in 2 h` | +| **Re‑engage** | Gentle nudge | `🚀 Welcome back bonus ready` | + +### 5 · Frequency & Control + +- **Start** at **≤ 1 push/day**; add more only if retention rises. +- Present a clear **opt‑out** path; trust drives long‑term engagement. + +### 6 · Copy Cheatsheet + +- **Lead with benefit**: "Earn 50 coins" beats "Check the app". +- **Curiosity**: "Something new awaits …". +- **Concrete numbers**: "30 s left" > "Hurry up". +- **Avoid** vague, generic, or feature‑only blasts. + +### 7 · Test & Improve + +| Variable | What to Try | +| -------------- | --------------------- | +| Emoji | 🔥 vs ⚡ | +| Timing | Instant vs +2 h | +| CTA | "Claim" vs "Open now" | +| Content length | Short vs rich preview | + +### 8 · Integrations + +- **Gamification** – Push on badge earned, streak day, leaderboard change. +- **Virality** – Notify referrer when invitee signs up or hits milestones. +- **Core value** – Release drops, new content, feature unlocks. + +### 9 · Home‑Screen Badge + +
+ Notification badges on home screen +
+ +Maintain ≥ 15 % opens to display this permanent badge—top‑of‑mind visibility every time users open World App. + +### 10 · Implementation Checklist + +- [ ] Ask permission **after** first value delivered +- [ ] Use `${username}` in every push +- [ ] Include `mini_app_path` deep link +- [ ] Limit to ≤ 1 notification/day at launch +- [ ] Monitor 7‑day open rate weekly +- [ ] A/B test copy monthly +- [ ] Iterate triggers; retire low performers diff --git a/mini-apps/guidelines/app-guidelines.mdx b/mini-apps/guidelines/app-guidelines.mdx new file mode 100644 index 0000000..f62b55e --- /dev/null +++ b/mini-apps/guidelines/app-guidelines.mdx @@ -0,0 +1,182 @@ +--- +title: "App Guidelines" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +## Mobile First + +Mini apps are inherently accessed via mobile, so your application UI should look and feel like a mobile app. + +### Key considerations for a mobile-first experience: + +- Use tab navigation to simplify movement within the app. +- Implement snap-to text boxes for easy user input. +- Avoid footers, sidebars, and excessive scrolling. +- Provide clear and direct navigation without hamburger menus. +- Ensure smooth transitions between different screens or sections. +- Use consistent background colors for a cohesive visual experience. +- Provide clear navigation cues to help users understand where they are and how to proceed. +- Ensure all UI elements are responsive and adapt well to different screen sizes. +- Use fonts that are optimized for readability on mobile devices. +- Include a splash page for sign-in if needed. + +
+
+ + ❌ Bad Example
Footer and long scrolling +
+
+ + ✅ Good Example
Bottom tab navigation and anchored buttons +
+
+## Scroll Bounce on IOS. + +We recommend you avoid scroll bounce error on iOS devices. Try disabling autoscroll & maybe fixed position elements or using 100dvh instead of 100vh. + +If you are not using a bottom navigation bar, you can use the following CSS to disable the scroll bounce error: + +```css +html, +body { + width: 100vw; + height: 100vh; + overscroll-behavior: none; + overflow: hidden; +} +``` + +Alternatively, you can try this approach which allows scrolling while still preventing the bounce effect: + +```css +html, +body { + -webkit-overflow-scrolling: touch; + overscroll-behavior: none; + overflow: scroll; +} +``` + +## Design Patterns + +Here are some design patterns that we recommend you follow: + +1. When a user is authenticated through their wallet, always show their username instead of the wallet address +2. Use [IDKit](https://docs.world.org/world-id/idkit/mini-apps) to confirm important actions or identity verification. +3. When dealing with wallet addresses, use an address book to link them to recognizable usernames or other identifiers + +## App Icon + +Your app icon should be a **square** image with a non white background. + +## Content Card + +Follow these guidelines when preparing the content card image: + +- Content card size is 345x240 px. +- Avoid adding text inside content card images as much as possible. +- Keep the bottom 94 px free of important details; this area is overlaid in app. +- The text and icon on the card are rendered from your app description and app icon automatically—no need to add them to the image. +- Export the banner without metadata, and include the blurred foreground element covering the bottom 94 px. +- Export without border radius, as PNG at 3x scale. + +
+ Good content card example + ✅ Good Example
+
+
+ +## Load times + +For mini apps, 2-3 seconds max for initial load and under 1 second for subsequent actions should be your target. +However, always test for real-world scenarios and provide visual feedback during loading to maintain user trust. + +## Branding & Identity + +Do not use the term "official" in your app name, description, or interface. Mini apps are third-party applications and should not create the impression that they are officially endorsed by or affiliated with World. +Mini apps should maintain their own distinct brand identity while integrating with the World ecosystem + +Additionally, do not use the **World logo** or any modified version of it in your app. + +## Chance based + +We recommend developers to avoid building chance based games, as these games have a very low likelihood of being approved. + +**Chance based** = prize awarded based on chance, not skill. This means you are using a RNG to determine a winner. +You can still have prizes but they need to be awarded based on skill. Not randomness. So winning a game where I get a prize is skill based. + +## Memberships & Yield + +Selling memberships or subscriptions that grant users access to increased yield, higher returns, or enhanced earning rates is strictly prohibited. Apps must not offer paid access to features or tiers that boost financial returns. + +## No Token Pre-sales + +Token pre-sales are strictly prohibited. Mini Apps must not offer, promote, or facilitate token pre-sales in any form. + +## NFTs on iOS + +Mini Apps may allow users to view their own NFTs, provided that NFT ownership does not unlock features or functionality within the app. +Mini Apps may allow users to browse NFT collections owned by others, provided that the mini apps may not include buttons, external links, or other calls to action that direct customers to purchasing mechanisms other than in-app purchase. + +## Localization + +Many of our users are located around the world. Apps that are localised for each region will perform significantly better. +You can recognize the user's locale by using the [Accept-Language](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) header + +For next.js apps, you can use [Next.js Internationalization](https://nextjs.org/docs/app/building-your-application/routing/internationalization) to localize your app. + +These languages are particularly important given our users: + +1. English +2. Spanish +3. Thai +4. Japanese +5. Korean +6. Portuguese + +## Usernames + +You should avoid displaying the user's wallet address, use their username instead. + +## Using the Address Book + +World ID inherently allows anonymity between applications. We generally encourage +developers to use their own Verify Command and verify the proof. However, we also offer a World ID +address book. This contract stores a mapping `addressVerifiedUntil` you can query to see if a World App +address is World ID Orb verified. + +## UI Kit + +The Mini Apps UI Kit is a React-based design system for accelerating the development of mini apps. It provides pre-built, reusable UI components that align with World's design guidelines for consistency and high-quality UX. + +### Get Started + +Install the UI Kit via NPM: + +```bash +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) +- [UI Kit Storybook](https://mini-apps-ui-kit.world.org) diff --git a/mini-apps/guidelines/design-guidelines.mdx b/mini-apps/guidelines/design-guidelines.mdx new file mode 100644 index 0000000..29304ca --- /dev/null +++ b/mini-apps/guidelines/design-guidelines.mdx @@ -0,0 +1,218 @@ +--- +title: Design Guidelines +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +This page gives you quick, visual rules to meet a high design standard for mini apps. Each image shows the intended pattern and a short usage note so you can apply it fast. + +## Grid and spacing + +**Usage** + +Padding is set to a default of 24px, providing ample breathing room around elements while maintaining a clean and structured appearance. +![Base unit](/images/docs/mini-apps/guidelines/base-unit.png) + +## Paddings + +**Usage** + +Padding is set to a default of 24px, providing ample breathing room around elements while maintaining a clean and structured appearance. +![Paddings](/images/docs/mini-apps/guidelines/paddings.png) + +## Navigation bar + +**Usage** + +Except for the control components of Mini Apps in the upper-right corner, all the other contents can be configured for custom designs. If interactive elements need to be set near the control components of Mini Apps, the developer shall note that whether the interaction area will conflict with the control components, and whether the operation is easy. +![Navigation bar](/images/docs/mini-apps/guidelines/navigation-bar.png) + +## Main pages + +
+
+ One clear task per page +

+ The default space between a header and its associated content is 16px, and + the same 16px spacing applies between elements within a section. This + consistent use of spacing helps create a clean visual structure and + predictable rhythm across the UI. +

+
+
+ Keep CTAs visible +

+ The default space between sections is set to 32px, helping to create clear + separation between distinct areas of content. The space between the search + bar and the content below is 24px, while the space between a sub-headline + and its associated content is 16px. +

+
+
+ Minimize unnecessary scrolling +

+ The default space between header and a section that starts with a sub + headline is set to 24px. +

+
+
+ Prioritize primary actions +

+ The default space between last item inside the scrollable area if a bottom + bar is presented is 32px. +

+
+
+ +## Secondary pages + +**Usage** + +The default space between a header and its associated content is 24px, while the space between a header and a secondary title is 32px. The space between secondary title and description should be 12px. + +
+ Secondary 1 + Secondary 2 +
+ +## Tab bar (Android vs iOS) + +**Usage** + +Navigation bar is positioned with a 12px space from the iOS & Android bottom bar, ensuring optimal accessibility and a comfortable tap area. + +
+
+ Tab bar Android +
+
+ Tab bar iOS +
+
+ +## Drawers / sheets (Android vs iOS) + +**Usage** + +Drawer is positioned with a 12px space from the iOS & Android bottom bar, ensuring optimal accessibility and a comfortable tap area. + +
+
+ Drawers Android +
+
+ Drawers iOS +
+
+ +## Toasts (Android vs iOS) + +**Usage** + +The toast message should be horizontally centered and positioned directly below the header to ensure visibility without disrupting user interaction. + +
+
+ Toast Android +
+
+ Toast iOS +
+
+ +## Keyboard handling (Android vs iOS) + +**Usage** + +Buttons are placed 24px above the active keyboard. This spacing ensures that buttons remain easily accessible and visually distinct, even when the keyboard is active, preventing accidental taps or overlap. + +
+
+ Keyboard Android +
+
+ Keyboard iOS +
+
+ +## Bottom safe area space (Android vs iOS) + +**Usage** + +Buttons are positioned with a 24px space from the iOS bottom bar, ensuring optimal accessibility and a comfortable tap area. + +
+
+ Bottom space Android +
+
+ Bottom space iOS +
+
+ +## States + +**Usage** + +Middle alignment ensures consistency across various screen sizes and component states, making the interface feel polished and intentional. This alignment strategy is especially effective for empty states, loading indicators, or other transient states, providing a seamless and cohesive user experience. +![Common states](/images/docs/mini-apps/guidelines/states.png) diff --git a/mini-apps/guidelines/features-and-guidelines.mdx b/mini-apps/guidelines/features-and-guidelines.mdx new file mode 100644 index 0000000..7dec1e4 --- /dev/null +++ b/mini-apps/guidelines/features-and-guidelines.mdx @@ -0,0 +1,85 @@ +--- +title: "Notification Guidelines" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +## Guidelines + +To maintain high-quality notifications, please adhere to the following guidelines: + +- Notifications should be purely functional, not marketing-related. +- Notifications must be directly related to the mini app. +- Notifications must be relevant to the user. + +## Username Substitution + +It's extremely easy to personalize your notifications, by using usernames. +Use the special string `${username}` in your notification message, and it will substitute in +the recipient's username. + + +```bash cURL +curl -X POST "https://developer.worldcoin.org/api/v2/minikit/send-notification" \ + -H "Authorization: Bearer {api_key}" \ + -H "Content-Type: application/json" \ + -d '{ + "app_id": "app_id", + "wallet_addresses": ["0x123", "0x456"], + "localisations": [ + { + "language": "en", + "title": "title", + "message": "🧑‍🍳 We're cooking something special for you ${username}" + } + ], + "mini_app_path": "worldapp://mini-app?app_id=[app_id]&path=[path]" + }' +``` + + +when sent to users, the message becomes: +`🧑‍🍳 We're cooking something special for you mistico` +`🧑‍🍳 We're cooking something special for you tute` +`🧑‍🍳 We're cooking something special for you struck` +and so on. + +
+
+ +## Notification Badges + +If a user has pending notifications for your miniapp, and you maintain +a 7 day rolling average open rate of 15% or higher, we'll show a badge on the user's home screen, +next to your miniapp's icon. + +It's cleared once the miniapp is opened. + +Notification badges increase your miniapp's visibility on the home screen, leading to higher user engagement and traffic. + +
+ +
+ Notification Interface in the Developer Portal +
+
+ +## Open Rate Goal + +An excellent goal to aim for is a 25% open rate. This % means your notifications drive strong traffic to the miniapp. +You can see this stat in your dashboard in Developer Portal. + +## Use Emoji & Curiosity in Titles + +Increases visual salience and emotion. +Examples: +`🔥 You're missing out on rewards` +`🕹️ A new level just unlocked` +`🤔 What's waiting inside the vault today?` + +## Useful links + +- [How To Send Notifications](/mini-apps/commands/how-to-send-notifications) +- [How To Request Notification Permissions](/mini-apps/commands/request-permission) +- [How To Get Notification Permissions](/mini-apps/commands/get-permissions) +- [Send Notification API Reference](https://docs.world.org/api-reference/developer-portal/send-notification) diff --git a/mini-apps/guidelines/policy.mdx b/mini-apps/guidelines/policy.mdx new file mode 100644 index 0000000..d01fe46 --- /dev/null +++ b/mini-apps/guidelines/policy.mdx @@ -0,0 +1,111 @@ +--- +title: "App Review Guidelines" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +World App seeks to foster a diverse and flourishing ecosystem of applications while at the same time ensuring users stay safe, and privacy is maximized. These applications are displayed to users within World App, but users interact with these applications either within a web browser or as a platform native application. World App has sole discretion of determining how apps are presented to users within its platform. + +These guidelines dictate the rules for apps which seek to be displayed to users from within World App. + +## MiniKit Integration + +Integrate MiniKit to supercharge your web app with exclusive features like World ID and Wallet access, making your mini app more engaging and valuable to users. + +To get your mini app approved, it's essential to use the MiniKit SDK commands effectively to enhance the user experience. +We're looking for meaningful integrations, whether through _World ID_ and _Wallet access_ , or other creative uses that add real value. + +## Safety + +The following content is not permitted for apps: + +- **Objectionable Content:** + - Defamatory, discriminatory, or mean-spirited content, including references or commentary about religion, race, sexual orientation, gender, national/ethnic origin, or other targeted groups + - Realistic portrayals of people or animals being killed, maimed, tortured, or abused, or content that encourages violence. + - Depictions that encourage illegal or reckless use of weapons and dangerous objects, or facilitate the purchase of firearms or ammunition. + - Overtly sexual or pornographic material, defined as "explicit descriptions or displays of sexual organs or activities intended to stimulate erotic rather than aesthetic or emotional feelings." + - NSFW (Not Safe For Work) content of any kind, including bots or AI generators that can produce, distribute, or facilitate access to NSFW content. + - Inflammatory religious commentary or inaccurate or misleading quotations of religious texts. + - False information and features, including inaccurate device data or trick/joke functionality, such as fake location trackers. + - Harmful concepts which capitalize or seek to profit on recent or current events, such as violent conflicts, terrorist attacks, and epidemics. + - Impersonating TFH or Worldcoin. +- **Physical Harm** + - If your app behaves in a way that risks physical harm, we may reject it. + - Apps that encourage consumption of tobacco and vape products, illegal drugs, or excessive amounts of alcohol are not permitted. + +## Naming + +Apps must comply with the below naming and description guidelines. + +- **App naming:** + - Keep names short and memorable for optimal recall and display. + - Don't use "World" in the name. + - Don't use descriptions as Mini App names. + - Don't use generic functional terms like "Earn", "Swap", etc. as your Mini App name. + - Don't try to mimic the name of a popular app, as this can lead to confusion. Apps may be rejected or penalized if their names mimic popular brands by simply swapping numbers or case. + - Do not include trademarked words, names of other apps, or explicit content. + - Exclude the use of special characters, or emojis. + - Ensure the name works well in your primary languages, and isn't offensive in other major markets. +- **App description:** + - Use plain, approachable language that clearly explains what the Mini App does. + - Describe the benefit or purpose in a straightforward way. + - Avoid spammy or misleading descriptions. + - Keep the tone aligned with World's voice: simple, human, and trustworthy. Avoid exaggerated, tech-heavy, or overly promotional phrasing. + - Write descriptions that are respectful, globally understandable, and free from language that might be offensive in other regions. + - Don't list functionality like "Earn", "Swap", or "Send" as standalone descriptions. Instead, explain how the app helps the user in their everyday life. + - Aim for one or two short sentences (under 25 words total) that communicate the app's value quickly. Users should understand the purpose at a glance e.g. Join real local meetups. No accounts, no spam, just one human per invite. + +## Legal + +Apps must comply with all legal requirements to be listed within World App. This includes the following: + +- **Privacy:** + - **Consent**: All apps that collect and store data from the user must request access and gain approval from the user before doing so + - **Data Minimization:** Apps should only request access to data that is relevant to how the app functions +- **Regulatory Compliance:** The application itself is responsible for maintaining regulatory compliance in all jurisdictions where it selects to be shown to users. Although the burden of compliance rests on the submitted application, if TFH determines that an application is non-compliant with jurisdiction(s) it has a right to take down the application and ask for it to be re-submitted in jurisdictions where it is compliant + +## App Submission + +Applications must be submitted for review on the developer platform. Before submission, please ensure the following: + +- Test your app for bugs +- Ensure that your app contains a live integration with a Worldcoin SDK, either IDkit or MiniKit +- Ensure that all app information in your dev portal submission is complete and accurate +- Ensure your contact information is updated so the review team can contact you +- Ensure your app can be accessed by the review team for testing +- Ensure your app complies with laws and regulations in the jurisdictions where it is shown to users. +- Check whether your app follows the [app guidelines](/mini-apps/guidelines/app-guidelines) +- Check whether your app follows the [smart contract development guidelines](/mini-apps/guidelines/smart-contract-development-guidelines) (if applicable) +- Check whether your app follows the [notification guidelines](/mini-apps/guidelines/features-and-guidelines) (if applicable) + +Ensure your app does not violate any of these guidelines, otherwise your app will be rejected. + +## App Review + +The review team has sole discretion of approving applications for Mini Apps. + +Apps will be reviewed as quickly as possible, though if the app is complex or difficult to test it may take some time. You will receive an email if the review status of your app changes, and you can view updates in the developer portal as well. + +Tools for Humanity will approve all submitted apps if it deems the following are true: + +- The data submitted in the form is complete and accurate +- The app is complete and contains all necessary copy and functionality to fulfil it’s purpose outlined in the submission +- The app is a final version, and is not a demo, trial or beta version +- The app contains a live integration with IDkit or MiniKit that functioned properly when tested +- The app abides by the safety and legal guidelines + +The review team will provide a rationale for any rejections, to which the developer can re-submit after remediating any concerns. If the application continuously fails review for the same reason, it may take longer for subsequent reviews to occur. + +Additionally, World App provides users the ability to report apps. If an app is in violation of the safety and legal guidelines or the review team deems the app should be removed for other reasons, the team has the sole discretion to remove an app from the platform. If an app is removed from the platform or an investigation is in progress, the development team will be alerted. + +## Technical Requirements + +- On Android and iOS, the World App Mini App should support operation under poor internet connections and handle temporary disconnections properly. +- The World App Mini App must be reliable, with no infinite loading during non-standard user actions. +- The World App Mini App must comply with the rules of both the Android and iOS app stores. +- The World App Mini App must not contain features that are unavailable on certain platforms. +- User progress must synchronize seamlessly between different platform versions. + +## User Support + +- Developers must provide a valid email address for user support and ensure accessible means of communication for resolving any issues that arise. diff --git a/mini-apps/guidelines/smart-contract-development-guidelines.mdx b/mini-apps/guidelines/smart-contract-development-guidelines.mdx new file mode 100644 index 0000000..64fa227 --- /dev/null +++ b/mini-apps/guidelines/smart-contract-development-guidelines.mdx @@ -0,0 +1,87 @@ +--- +title: "Smart Contract Development Guidelines" +--- + +These guidelines define the minimum requirements for developing smart contracts for deployment & use within the World App Mini App Store. They are designed to protect users from malicious or negligent behaviors, especially in contracts that custody user funds. + +## Applicability + +These requirements apply to **all** smart contracts submitted to the Mini App Store that **custody, lock, or manage user-owned assets** (fungible tokens, NFTs, or other on-chain value). + +Failure to comply may result in rejection or removal from the platform. + +## Custody & Upgradeability Rules + +### Non-Upgradeable Custody Contracts + +- **Requirement:** Custody contracts must be **immutable** after deployment. + +### Controlled Upgradeability (If Needed) + +- If upgradeability is essential (e.g., for bug fixes), it must: + - Use a **2-of-2 multi-signature upgrade authorization** where **Tools For Humanity (TFH)** holds one key. + - Require **TFH review and written approval** before any upgrade is executed. + - Be subject to a **public notice period** (recommended: 48–72 hours) before upgrade execution. + +## Restricted Owner Privileges + +### Prohibition of User Asset Withdrawal by Owner + +- **No direct owner/admin functions** may exist that allow the developer (or any third party) to: + - Transfer, withdraw, or seize assets deposited by users. + - Change accounting logic in a way that reassigns user balances to the developer. + +### Exceptions + +- **Permitted:** Functions to withdraw **protocol fees** or **platform earnings** that are: + - Clearly documented. + - Separately accounted for from user funds. + - Agreed upon during code review. + +## Code Quality & Review Requirements + +### Testing + +- Contracts must include **comprehensive automated tests**, ideally in a **Foundry** or similar high-quality test framework. +- Tests must cover: + - Normal operation flows. + - Edge cases and failure modes. + - Security-critical logic (access control, withdrawals, deposits, upgrades). +- Code coverage target: **≥90%** for critical custody logic. + +### Repository Access + +- Source code must be stored in a **version-controlled repository**. +- The repository must be **shared with the TFH review team** prior to deployment. + +### Documentation + +- Contracts must be **fully documented** with: + - **NatSpec comments** for all public/external functions. + - Clear explanation of any access control roles and permissions. + - Rationale for any upgradeability or special privilege mechanisms. + +## Security Best Practices + +### Recommended Patterns + +- Inherit [**OpenZeppelin contracts**](https://github.com/OpenZeppelin/openzeppelin-contracts) for all token/contract standards +- Follow [**Checks-Effects-Interactions**](https://fravoll.github.io/solidity-patterns/checks_effects_interactions.html) pattern ubiquitously + +### Audits + +- High-value custody contracts are **strongly recommended** to undergo an **independent security audit**. +- Audit reports should be shared with TFH prior to mainnet deployment. + +### Common Pitfalls to Avoid + +- No unbounded loops over user-controlled data. +- No hardcoded privileged addresses. +- Avoid arbitrary external call execution (`call`, `delegatecall`) in external facing functions unless strictly necessary and reviewed. + +## Compliance & Enforcement + +- Contracts failing to comply with these guidelines may be: + - Rejected during review. + - Removed from the Mini App Store. +- TFH reserves the right to request code changes or audits before approval. diff --git a/mini-apps/index.mdx b/mini-apps/index.mdx new file mode 100644 index 0000000..17596df --- /dev/null +++ b/mini-apps/index.mdx @@ -0,0 +1,35 @@ +--- +title: "What are Mini Apps?" +description: "Learn what Mini Apps are and how to build native-like apps in World App with MiniKit, distribution and monetization." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/mini-apps-meta.png" +"og:description": "What Mini Apps are and how to build them with MiniKit." +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/mini-apps-meta.png" +--- + +Mini apps enable third-party developers to create native-like applications within World App. +Best for small and medium sized developers who want access to a distribution channel of millions of users. +Build with flexible smart contracts and leverage World ID and the World Wallet for seamless user experiences. + +## How it Works + +Mini apps are web applications running inside World App via webview. Using the MiniKit SDK, these applications can become +native-like and interact with the World ecosystem. + +## Quick links + + + + Statistics about the Mini Apps ecosystem. + + + Check the current status of World App. + + diff --git a/mini-apps/migration/minikit-v2.mdx b/mini-apps/migration/minikit-v2.mdx new file mode 100644 index 0000000..d5083f9 --- /dev/null +++ b/mini-apps/migration/minikit-v2.mdx @@ -0,0 +1,74 @@ +--- +title: "MiniKit 2.0 Migration" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +MiniKit 2.x consolidates command handling around async `MiniKit` methods and removes World ID verification from MiniKit. + +## Breaking Changes + +- World ID verification moved out of MiniKit and into [`@worldcoin/idkit`](https://docs.world.org/world-id/idkit/mini-apps) +- Commands moved to top-level async methods on `MiniKit` instead of `commandsAsync` and `commands` +- 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 +- `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 + +## World ID Changes + +Any old `MiniKit.verify` or `MiniKit.commandsAsync.verify` flow should be replaced with IDKit. MiniKit 2.x only owns mini app commands. See [IDKit for Mini Apps](https://docs.world.org/world-id/idkit/mini-apps) for the current integration shape. + +## Old To New + +```ts +// 1.x +MiniKit.commands.signMessage({ message: "hello" }); +await MiniKit.commandsAsync.walletAuth({ nonce }); +await MiniKit.commandsAsync.sendTransaction({ transaction: [tx] }); + +// 2.x +await MiniKit.signMessage({ message: "hello" }); +await MiniKit.walletAuth({ nonce }); +await MiniKit.sendTransaction({ + chainId: 480, + transactions: [{ to, data, value }], +}); +``` + +## Import Path Changes + +```ts +import type { MiniKitSendHapticFeedbackOptions } from "@worldcoin/minikit-js/commands"; +import { getIsUserVerified } from "@worldcoin/minikit-js/address-book"; +import { verifySiweMessage } from "@worldcoin/minikit-js/siwe"; +``` + +## `walletAuth` Changes +- Validation now requires you to strip hyphens from UUID-based nonces + +```ts +const nonce = crypto.randomUUID().replace(/-/g, ""); +``` + +## `sendTransaction` Migration + +- No longer uses abi encoding and instead expects pre-encoded calldata in the `transactions` array +- Poll transaction progress with `userOpHash` + +```ts +const result = await MiniKit.sendTransaction({ + chainId: 480, + transactions: [ + { + to: "0x...", + data: "0x...", + value: "0x0", + }, + ], +}); + +console.log(result.data.userOpHash); +``` diff --git a/mini-apps/migration/standalone-dapp.mdx b/mini-apps/migration/standalone-dapp.mdx new file mode 100644 index 0000000..7b241e2 --- /dev/null +++ b/mini-apps/migration/standalone-dapp.mdx @@ -0,0 +1,167 @@ +--- +title: "Upgrade to a Standalone Web App" +description: "Convert your mini app into a standalone web app that works both inside World App and in any browser." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +MiniKit commands auto-detect the environment. Outside World App, they fall back to Wagmi. + +| Feature | Effort | Details | +|---|---|---| +| World ID | None | IDKit works out of the box | +| Auth | Add Wagmi + providers | Wagmi handles SIWE on web | +| Transactions | Add Wagmi + branch receipts | Hash type differs per environment | +| Other commands | Add `fallback` function | Custom logic per command | + + +## 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 +``` + +## 1. Install Dependencies + +```bash +pnpm add wagmi @tanstack/react-query siwe +``` + +## 2. Wagmi Config + +```ts title="config.ts" +import { worldApp } from "@worldcoin/minikit-js/wagmi"; +import { createConfig, http } from "wagmi"; +import { worldchain } from "wagmi/chains"; +import { injected } from "wagmi/connectors"; + +export const config = createConfig({ + chains: [worldchain], + connectors: [worldApp(), injected()], + transports: { + [worldchain.id]: http(), + }, +}); +``` + +## 3. Providers + +```tsx title="providers.tsx" +"use client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { MiniKitProvider } from "@worldcoin/minikit-js/minikit-provider"; +import { WagmiProvider } from "wagmi"; +import { config } from "./config"; + +const queryClient = new QueryClient(); + +export default function Providers({ children }: { children: React.ReactNode }) { + return ( + + + + {children} + + + + ); +} +``` + + + The `worldApp()` connector automatically registers the wagmi fallback. If your app uses wagmi **without** the `worldApp()` connector (e.g. a pure-web setup that still wants MiniKit's fallback), you need to explicitly register it: + + ```ts + import "@worldcoin/minikit-js/wagmi-fallback"; + ``` + + Or call `registerWagmiFallback(config)` from the same subpath. + + +## World ID + +No changes. [IDKit](https://docs.worldcoin.org/world-id/quick-start) is independent of the wallet layer. + +## Auth + +`MiniKit.walletAuth()` works automatically. No code changes needed. + +```tsx +const result = await MiniKit.walletAuth({ nonce, statement: "Sign in" }); +// result.executedWith === "minikit" (World App) or "wagmi" (web) +``` + +### Backend Verification + +`verifySiweMessage` handles both Smart Accounts (EIP-1271) and EOAs (ECDSA) automatically: + +```ts title="api/verify.ts" +import { verifySiweMessage } from "@worldcoin/minikit-js/siwe"; + +const { isValid, siweMessageData } = await verifySiweMessage(payload, nonce); +``` + +## Transactions + +`MiniKit.sendTransaction()` works automatically. World Chain only (chainId 480). + +- **World App**: native bridge, atomic batching, returns `userOpHash` +- **Web (single tx)**: sent directly via Wagmi +- **Web (multiple txs)**: executed sequentially — each requires wallet confirmation and is not atomic + +```tsx +const result = await MiniKit.sendTransaction({ + chainId: 480, + transactions: [ + { to: "0x...", data: encodeFunctionData({ abi, functionName: "mint", args: [] }) }, + ], +}); +``` + +### Receipts + +Branch on `result.executedWith` — World App returns a UserOperation hash, web returns a standard tx hash: + +```tsx +import { useUserOperationReceipt } from "@worldcoin/minikit-react"; + +// For World App: poll for the UserOperation receipt +const { poll } = useUserOperationReceipt({ client }); + +if (result.executedWith === "minikit") { + await poll(result.data.userOpHash); +} else { + // Web: standard tx hash, use wagmi or viem directly + await publicClient.waitForTransactionReceipt({ + hash: result.data.userOpHash as `0x${string}`, + }); +} +``` + +## Other Commands + +Commands without Wagmi fallbacks (`pay`, `shareContacts`, etc.) need a `fallback`: + +```tsx +import { Tokens, tokenToDecimals } from "@worldcoin/minikit-js/commands"; + +const result = await MiniKit.pay({ + to: "0xRecipientAddress", + tokens: [ + { + symbol: Tokens.USDC, + token_amount: tokenToDecimals(1, Tokens.USDC).toString(), + }, + ], + reference: "order-123", + description: "Coffee", + fallback: async () => myCustomPaymentFlow(), +}); +``` + +Without a fallback, these throw `CommandUnavailableError` on web. diff --git a/mini-apps/migration/web-to-miniapp.mdx b/mini-apps/migration/web-to-miniapp.mdx new file mode 100644 index 0000000..d6b5ec6 --- /dev/null +++ b/mini-apps/migration/web-to-miniapp.mdx @@ -0,0 +1,154 @@ +--- +title: "Migrate a Web App to Mini App" +description: "Convert an existing Next.js web app to work as a World App mini app." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Convert an existing Next.js web app that uses viem to work as a World App mini app. + +## 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 +``` + +## 1. Install MiniKit + +```bash +pnpm add @worldcoin/minikit-js @worldcoin/minikit-react +``` + +## 2. Disable SSR + +MiniKit depends on `window.WorldApp`. SSR causes hydration mismatches that silently break event handlers. + +```tsx title="src/app/page.tsx" +"use client"; +import dynamic from "next/dynamic"; +const App = dynamic(() => import("../components/App"), { ssr: false }); +export default function Page() { + return ; +} +``` + +## 3. Add MiniKitProvider + +```tsx title="src/app/providers.tsx" +"use client"; +import { MiniKitProvider } from "@worldcoin/minikit-js/minikit-provider"; + +export default function Providers({ children }: { children: React.ReactNode }) { + return {children}; +} +``` + +Wrap children in your layout: + +```tsx title="src/app/layout.tsx" +import Providers from "./providers"; + + + {children} + +``` + +## 4. Wallet Connection + +Use `getWorldAppProvider()` inside World App, fall back to `window.ethereum` for browsers. + +```tsx +import { MiniKit, getWorldAppProvider } from "@worldcoin/minikit-js"; +import { createWalletClient, custom } from "viem"; +import { worldchain } from "viem/chains"; + +const provider = MiniKit.isInWorldApp() + ? getWorldAppProvider() + : window.ethereum; + +const walletClient = createWalletClient({ + chain: worldchain, + transport: custom(provider), +}); +``` + +Under the hood, `getWorldAppProvider()` maps: +- `eth_requestAccounts` → `MiniKit.walletAuth()` +- `eth_sendTransaction` → `MiniKit.sendTransaction()` +- `eth_chainId` → `0x1e0` (World Chain 480) + +Existing `writeContract` / `readContract` calls work unchanged. + +## 5. Bundle Approve + Contract Calls + +World App resets approvals to 0 after each transaction. A separate `approve()` tx followed by `transferFrom()` in the next tx will fail. + +Bundle them in one call: + +```tsx +import { MiniKit } from "@worldcoin/minikit-js"; +import { encodeFunctionData } from "viem"; + +await MiniKit.sendTransaction({ + chainId: 480, + transactions: [ + { + to: TOKEN, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "approve", + args: [CONTRACT, amount], + }), + }, + { + to: CONTRACT, + data: encodeFunctionData({ + abi: contractAbi, + functionName: "swap", + args: [amount], + }), + }, + ], +}); +``` + +World App executes these atomically. On web, they execute sequentially — each requires a separate wallet confirmation and is not atomic. + +## 6. Handle userOpHash Receipts + +MiniKit returns a `userOpHash`, not a standard tx hash. Use `useUserOperationReceipt` from `@worldcoin/minikit-react` to poll for the receipt: + +```tsx +import { useUserOperationReceipt } from "@worldcoin/minikit-react"; +import { createPublicClient, http } from "viem"; +import { worldchain } from "viem/chains"; + +const client = createPublicClient({ + chain: worldchain, + transport: http(), +}); + +const { poll, isLoading } = useUserOperationReceipt({ client }); + +// After sendTransaction: +const result = await MiniKit.sendTransaction({ ... }); +await poll(result.data.userOpHash); +``` + +## 7. Whitelist Contracts and Tokens + +In **Developer Portal > Mini App > Permissions**, add: + +- **Permit2 Tokens** — every ERC-20 your app transfers +- **Contract Entrypoints** — every contract your app calls + +Non-whitelisted contracts are blocked with `invalid_contract`. + +## Common Gotchas + +| Issue | Symptom | Fix | +|---|---|---| +| SSR hydration mismatch | Clicks do nothing | `dynamic(..., { ssr: false })` | +| Approval reset after tx | `transferFrom` reverts | Bundle approve + call in one `sendTransaction` | +| `userOpHash` not a tx hash | `waitForTransactionReceipt` times out | Use `useUserOperationReceipt` from `@worldcoin/minikit-react` | +| Missing contract whitelist | `invalid_contract` error | Add to Developer Portal | diff --git a/mini-apps/more/community-tools-perks.mdx b/mini-apps/more/community-tools-perks.mdx new file mode 100644 index 0000000..b484ff6 --- /dev/null +++ b/mini-apps/more/community-tools-perks.mdx @@ -0,0 +1,45 @@ +--- +title: "Community Perks" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Special perks and integrations from trusted providers supporting the World Mini App ecosystem. + + + + Pilot program rewarding qualifying Mini App developers based on verified + human usage. $300K USD equivalent in WLD over three months. + + + Foundation Grants supporting the World Network and novel mini apps (50M WLD + dedicated). [Learn more](https://world.org/grants) + + + 4 months free for Enterprise, 3 months free for PAYG, $500–$2000 in bonus + incentives, plus co-marketing and onsite opportunities + + + Apply for Cloudflare for Startups program. Use 'World Mini App' as your + partner when applying. + + + 15% discount on any Lazo product, plus 6 months free of Lazo One (Data Room + + AI for fundraising). + + + 30 day free trial for all builders on World Chain. Use code: TW-WORLDCHAIN + + diff --git a/mini-apps/more/faq.mdx b/mini-apps/more/faq.mdx new file mode 100644 index 0000000..131db25 --- /dev/null +++ b/mini-apps/more/faq.mdx @@ -0,0 +1,87 @@ +--- +title: "FAQ" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +## Who covers gas fees for transactions? + +World App sponsors gas fees for most transactions on Worldchain, subject to transaction minimums and restrictions. +When someone verifies their identity with World ID and uses the World App, their transactions on Worldchain are automatically covered by the network. +This means users don't need to hold or spend ETH to interact with mini apps, send tokens, or perform on-chain actions. +The sponsorship is handled behind the scenes with a paymaster contract that pays the gas fees whenever a verified user initiates a transaction. + +## Do I need approval to launch a Mini App? + +Yes. After development, submit your Mini App for review through the [Developer Portal](https://developer.worldcoin.org/). Approval is required before it's publicly listed. + +## How does Send transaction command work? + +The [Send Transaction command](/mini-apps/commands/send-transaction) is designed to execute on-chain transactions from the Mini App. When developers invoke this command, +they supply a `chainId` and one or more `transactions` containing the target contract address, optional encoded function call (`data`), and optional value. +For security, World App requires that any smart contract or token interactions are pre-approved (whitelisted) in the Developer Portal to prevent unauthorized transfers. +For ERC-20 token transfers, standard `approve()` calls work, but Permit2 is recommended. World App automatically approves tokens to the Permit2 contract, so you can bundle the Permit2 approval and your contract call in a single transaction for a better UX. + +## How does Sign Message command work? + +The [Sign Message command](/mini-apps/commands/sign-message) prompts the user to cryptographically sign an arbitrary message using +their wallet's private key. When you call the command (for example, `await MiniKit.signMessage({ message: "Hello" })`), World App generates a standardized payload that may include a nonce, timestamp, and other context to prevent replay attacks. +The user's wallet then signs this message using ECDSA, producing a signature that proves the user controls the wallet, all without exposing the private key. +This signed message can be sent back to your backend for verification, ensuring the integrity of user actions and serving as secure off-chain authorization for various operations. + +## Can I use the simulator to test transactions on mini apps? + +No, mini app needs to be developed on mainnet (we don't support testnet). Gas is covered in the World App, so there's only deployment costs for you to develop on mainnet. Deploy "test" contracts to mainnet, and then redeploy "prod" mainnet contracts. + +## How do I debug send transaction failed simulation? + +Use the [Get Transaction Debug URL endpoint](https://docs.world.org/api-reference/developer-portal/get-transaction-debug-url) to get the debug URL for the transaction. + +## How do I check how commands work and it's implementation? + +Check the [Prod QA App](https://world.org/ecosystem/app_dfbe55706a640c82dce839bb0ecae74d) to check commands and the [minikit-js](https://github.com/worldcoin/minikit-js) repository. + +## How do I test my mini app on mobile? + +Remember that minikit ONLY works inside World App, so in order to test your mini app commands you must open it in World App + +To test your Mini App directly on your phone, expose your app publicly using ngrok or any other tunneling service. After the URL is generated, go to the [Developer Portal](https://developer.worldcoin.org/) to configure it. + +Example: + +```bash +ngrok http http://localhost:3000 +``` + +## Why does my command fail when triggered immediately on page load? + +This happens due to a race condition where MiniKit hasn't finished installing when you try to call a command. Since MiniKit uses a client-side component to install the provider onto the Window object, triggering a command in a separate useEffect hook right after page load can fail. + +Solution: Include any commands you want to trigger on initialization inside the same useEffect hook where you install MiniKit: + +```tsx +"use client"; + +import { useEffect, ReactNode } from "react"; +import { MiniKit } from "@worldcoin/minikit-js"; + +export const MiniKitProvider = ({ children }: { children: ReactNode }) => { + useEffect(() => { + const { success } = MiniKit.install(); + + if (!success) return; + + // Add any commands you wish to trigger on start here to prevent race conditions + void MiniKit.getPermissions(); + }, []); + + return <>{children}; +}; +``` + +## What security considerations should I keep in mind when building Mini Apps? + +MiniKit is purely a communication channel between the client and the app. Your application should never trust any payloads it receives on the client side by default. + +World ID verification via IDKit, Pay, and Wallet Auth operations should be verified on your backend. Never rely solely on client-side validation for sensitive operations, as client-side data can be manipulated. +Always implement server-side verification to ensure the integrity and authenticity of user actions. diff --git a/mini-apps/more/webview-spec.mdx b/mini-apps/more/webview-spec.mdx new file mode 100644 index 0000000..a408e40 --- /dev/null +++ b/mini-apps/more/webview-spec.mdx @@ -0,0 +1,35 @@ +--- +title: "Webview Specifications" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + + +The widget is opened within the World App via a WebView. This means providers can tailor their solutions by considering the specific features and restrictions of these platforms. + +### **Capabilities:** + +- **WebView Engine:** + - **Android:** Uses Android's native WebView implementation. + - **iOS:** Uses **WKWebView**, the recommended web rendering engine on iOS, offering enhanced security and performance. +- **File System and Camera Access:** + - Access to the camera and file system (e.g., for file uploads) is possible if the user grants permission. +- **Cookies and DOM Storage:** + - Supported on both platforms with explicit activation for Android and default behavior for iOS. +- **Location:** + - Location is supported on both platforms. + ```javascript + navigator.geolocation.getCurrentPosition((position) => { + console.log(position); + }); + ``` + +### **Restrictions:** + +- **New Windows:** + - Opening new browser windows is prohibited. All navigation remains within the current WebView instance. +- **Zooming:** + - **Android:** Not restricted by default. + - **iOS:** Disabled. +- **Alert Dialogs:** + - Alert Dialogs are not supported on iOS \ No newline at end of file diff --git a/mini-apps/quick-start/app-store.mdx b/mini-apps/quick-start/app-store.mdx new file mode 100644 index 0000000..5009386 --- /dev/null +++ b/mini-apps/quick-start/app-store.mdx @@ -0,0 +1,15 @@ +--- +title: "Mini App Store" +description: "Submit your Mini App for review and distribution in World App; follow guidelines and resolve rejections." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +To ensure a consistent user experience, apps will be rejected unless they follow our [guidelines](/mini-apps/guidelines/app-guidelines). + +Once your mini app is ready to be published, you can submit it for review inside of the Developer Portal. +Once approved, your mini app will be available to all World App users to discover in the Mini Apps. + +![Submit an App](/images/docs/mini-apps/quick-start/submit-app.png) + +If your app was rejected please reach out to @MateoSauton on Telegram for more information. diff --git a/mini-apps/quick-start/commands.mdx b/mini-apps/quick-start/commands.mdx new file mode 100644 index 0000000..4842b06 --- /dev/null +++ b/mini-apps/quick-start/commands.mdx @@ -0,0 +1,107 @@ +--- +title: "Commands" +description: "Overview of current MiniKit commands for mini apps." +--- + +import { QRCodeGenerator } from "/snippets/qr-code-generator.jsx"; + +Commands are async methods on `MiniKit`. Use `await MiniKit.()` and handle the returned `{ executedWith, data }` result. + + + World ID verification is no longer a MiniKit command. Use + [`@worldcoin/idkit`](https://docs.world.org/world-id/idkit/mini-apps) for new verification flows. + + +Try our preview mini app to get a sense of how commands work. + +{/* + The QR section below is wrapped in (rendered on the + web, excluded from the copy-page/.md export) with a plain-markdown twin in + (excluded from the web, rendered in the export). + The snippet import is safe ONLY because the usage sits inside the for="humans" + block: Mintlify inlines a snippet's full source into the export when the + component appears in exported content, and drops it when all usages are hidden + (verified on a preview deploy). Keep the twin in sync when editing. +*/} + + + Scan the QR code below with your phone (you must have World App installed). + +
+ +
+
+ + + Open [https://world.org/mini-app?app_id=app_dfbe55706a640c82dce839bb0ecae74d](https://world.org/mini-app?app_id=app_dfbe55706a640c82dce839bb0ecae74d) on a phone with World App installed (the docs page renders this link as a QR code). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
PayRequest a payment inside World App
Wallet AuthAuthenticate with Sign-In with Ethereum
Send TransactionSubmit one or more World Chain transactions
Sign MessageSign an EIP-191 personal message
Sign Typed DataSign an EIP-712 typed payload
Share ContactsOpen the World App contact picker
Request PermissionRequest notifications or microphone access
Get PermissionsRead current mini app permission state
Send Haptic FeedbackTrigger native haptic feedback
ShareOpen the native share sheet
World ChatOpen World Chat with a prefilled message
AttestationRequest an app attestation token
Close Mini AppProgrammatically close the mini app
+ +Push notifications are documented separately in [Send Notifications](/mini-apps/commands/how-to-send-notifications), since they combine permission handling with Developer Portal setup. diff --git a/mini-apps/quick-start/init.mdx b/mini-apps/quick-start/init.mdx new file mode 100644 index 0000000..2eccede --- /dev/null +++ b/mini-apps/quick-start/init.mdx @@ -0,0 +1,215 @@ +--- +title: Initialization +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +When your mini app initializes inside World App, MiniKit stores device context, launch context, and basic user metadata on the client. + +If you are using React, `MiniKitProvider` will perform the initialization for you. + +Otherwise, manually initialize MiniKit at the start of your app: +```tsx +import { MiniKit } from "@worldcoin/minikit-js"; + +const { success } = MiniKit.install(); +``` + + +## MiniKit State + +After install, these are the public MiniKit state accessors you can rely on: + + + + ```tsx + // MiniKit state + { + user: { + walletAddress?: string; + username?: string; + profilePictureUrl?: string; + permissions?: { + notifications: boolean; + contacts: boolean; + }; + optedIntoOptionalAnalytics?: boolean; + verificationStatus?: { + isOrbVerified: boolean; + isDocumentVerified: boolean; + isSecureDocumentVerified: boolean; + }; + preferredCurrency?: string; + pendingNotifications?: number; + }; + deviceProperties: { + safeAreaInsets?: { + top: number; + right: number; + bottom: number; + left: number; + }; + deviceOS?: string; + worldAppVersion?: number; + }; + location: "chat" | "home" | "app-store" | "deep-link" | "wallet-tab" | null; + } + ``` + + + +Notes: + +- `walletAddress`, `verificationStatus`, `preferredCurrency`, `pendingNotifications`, and `optedIntoOptionalAnalytics` are all available at initialization. +- `username` and `profilePictureUrl` are populated after `walletAuth()`. +- `MiniKit.location` is the mapped launch location. Use this instead of reading older launch-origin fields directly. + +## Permissions + +`MiniKitProvider` installs MiniKit, but it does not automatically fetch permission state. + +If you need the current permission settings, call `MiniKit.getPermissions()` explicitly: + +```tsx +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; +``` + +Notes: + +- `MiniKit.user.permissions` is cached MiniKit state and should be treated as incomplete until you fetch permissions. +- `getPermissions()` can return `notifications`, `contacts`, and `microphone`. +- The cached `MiniKit.user.permissions` shape only exposes `notifications` and `contacts`. + +## Launch Location + +MiniKit normalizes the raw World App launch origin into: + + + + ```tsx + type MiniAppLaunchLocation = + | "chat" + | "home" + | "app-store" + | "deep-link" + | "wallet-tab" + | null; + ``` + + + + ```tsx + import { MiniKit } from "@worldcoin/minikit-js"; + + if (MiniKit.location === "chat") { + console.log("Opened from chat"); + } + ``` + + + +## Raw World App Object + +If you need the untransformed World App payload, read `window.WorldApp` directly. + + + + ```tsx + // window.WorldApp + { + world_app_version: number; + device_os: "ios" | "android"; + is_optional_analytics: boolean; + wallet_address: string; + verification_status: { + is_orb_verified: boolean; + is_document_verified: boolean; + is_secure_document_verified: boolean; + }; + preferred_currency: string; + pending_notifications: number; + supported_commands: Array<{ + name: + | "verify" + | "attestation" + | "pay" + | "wallet-auth" + | "send-transaction" + | "sign-message" + | "sign-typed-data" + | "share-contacts" + | "request-permission" + | "get-permissions" + | "send-haptic-feedback" + | "share" + | "chat" + | "close-miniapp" + | "microphone-stream-started" + | "microphone-stream-ended"; + supported_versions: number[]; + }>; + safe_area_insets: { + top: number; + right: number; + bottom: number; + left: number; + }; + location: { + open_origin: string; + } | null | undefined; + } + ``` + + + + ```json + { + "world_app_version": 4001000, + "device_os": "ios", + "is_optional_analytics": true, + "wallet_address": "0x377da9cab87c04a1d6f19d8b4be9aef8df26fcdd", + "verification_status": { + "is_orb_verified": true, + "is_document_verified": true, + "is_secure_document_verified": false + }, + "preferred_currency": "USD", + "pending_notifications": 0, + "supported_commands": [ + { "name": "verify", "supported_versions": [1] }, + { "name": "attestation", "supported_versions": [1] }, + { "name": "pay", "supported_versions": [1] }, + { "name": "wallet-auth", "supported_versions": [1, 2] }, + { "name": "send-transaction", "supported_versions": [1, 2] }, + { "name": "sign-message", "supported_versions": [1] }, + { "name": "sign-typed-data", "supported_versions": [1] }, + { "name": "share-contacts", "supported_versions": [1] }, + { "name": "request-permission", "supported_versions": [1] }, + { "name": "get-permissions", "supported_versions": [1] }, + { "name": "send-haptic-feedback", "supported_versions": [1] }, + { "name": "share", "supported_versions": [1] }, + { "name": "chat", "supported_versions": [1] }, + { "name": "close-miniapp", "supported_versions": [1] }, + { "name": "microphone-stream-started", "supported_versions": [1] }, + { "name": "microphone-stream-ended", "supported_versions": [1] } + ], + "safe_area_insets": { + "top": 0, + "right": 0, + "bottom": 0, + "left": 0 + }, + "location": { + "open_origin": "deeplink" + } + } + ``` + + + +Use `window.WorldApp` only when you need the raw payload. In application code, prefer `MiniKit.user`, `MiniKit.deviceProperties`, and `MiniKit.location`. diff --git a/mini-apps/quick-start/installing.mdx b/mini-apps/quick-start/installing.mdx new file mode 100644 index 0000000..8c8b8e6 --- /dev/null +++ b/mini-apps/quick-start/installing.mdx @@ -0,0 +1,150 @@ +--- +title: "Getting Started" +description: "Create a Mini App with the official template or install MiniKit-JS manually." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +[MiniKit-JS](https://github.com/worldcoin/minikit-js) is our official SDK for creating mini apps that work with World App. + +## 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. + +```bash +npx @worldcoin/create-mini-app@latest my-first-mini-app +``` + +
+ Template +

+ Correctly running template should look like this +

+
+ +## Manual Installation + +[MiniKit-JS](https://www.npmjs.com/package/@worldcoin/minikit-js) is the core library and is framework agnostic. Install it with `pnpm`, or use a CDN like [jsdelivr](https://www.jsdelivr.com/package/npm/@worldcoin/minikit-js) for inline HTML. Replace `[version]` with the release you want to load. + + +```bash title="pnpm" +pnpm install @worldcoin/minikit-js +``` + +```html title="jsDelivr CDN" + +``` + + +## Usage + +1. Wrap your app with `MiniKitProvider` in a client component. This initializes MiniKit and makes it available throughout your app. + +```tsx src/app/providers.tsx +"use client"; + +import { MiniKitProvider } from "@worldcoin/minikit-js/minikit-provider"; + +export default function Providers({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} +``` + +```tsx src/app/layout.tsx +import Providers from "./providers"; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {children} + + + ); +} +``` + +2. Check if MiniKit is installed. +`MiniKit.isInstalled()` will only return true if the mini app is opened and initialized inside the World App. This is useful to distinguish between a user opening your app in the browser or in the World App. + +```tsx +import { MiniKit } from "@worldcoin/minikit-js"; + +console.log(MiniKit.isInstalled()); +``` + +## Build with AI + +The [World Docs MCP](https://docs.world.org/model-context-protocol/world-docs) lets any coding assistant search the World documentation to help you build your mini app. + + + + ```bash + claude mcp add --transport http world https://docs.toolsforhumanity.com/mcp + ``` + + + ```bash + codex mcp add --transport http world https://docs.toolsforhumanity.com/mcp + ``` + + + Add to `.cursor/mcp.json`: + ```json + { + "mcpServers": { + "world": { + "url": "https://docs.toolsforhumanity.com/mcp" + } + } + } + ``` + + + Add to `.vscode/mcp.json`: + ```json + { + "servers": { + "world": { + "type": "http", + "url": "https://docs.toolsforhumanity.com/mcp" + } + } + } + ``` + + + +## Template Repositories + +The following template repositories are also available: + +- [Vanilla JS (using a CDN) template (featuring a simple backend for verifications)](https://github.com/new?template_name=minikit-js-template&template_owner=worldcoin), +- [Community example - Wallet Auth using JWT](https://github.com/wlding-blocks/wld-mini-apps-101). +- [Community example - Wallet Auth using NextAuth](https://github.com/supercorp-ai/minikit-wallet-auth-next-auth). + +Otherwise, continue below with the installation instructions. + + + Watch a video tutorial [here](https://www.youtube.com/watch?v=QJ0htHP6lb0). + diff --git a/mini-apps/quick-start/responses.mdx b/mini-apps/quick-start/responses.mdx new file mode 100644 index 0000000..7fa6347 --- /dev/null +++ b/mini-apps/quick-start/responses.mdx @@ -0,0 +1,51 @@ +--- +title: "Responses" +description: "Handle MiniKit command results with `{ executedWith, data }` and standard error handling." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Modern MiniKit commands are async methods on `MiniKit`. They resolve to a result object shaped like `{ executedWith, data }` and throw if the command fails. + +## Async Command Results + +Use `executedWith` to understand whether the command ran through World App, Wagmi, or a custom fallback. + +```tsx +import { MiniKit } from "@worldcoin/minikit-js"; +import type { + CommandResultByVia, + MiniKitWalletAuthOptions, + WalletAuthResult, +} from "@worldcoin/minikit-js/commands"; + +async function signInWithWallet() { + const input = { + nonce: "randomnonce123", + } satisfies MiniKitWalletAuthOptions; + + try { + const result: CommandResultByVia = + await MiniKit.walletAuth(input); + + console.log(result.executedWith); + // "minikit" | "wagmi" | "fallback" + + console.log(result.data.address); + console.log(result.data.signature); + } catch (error) { + console.error("Command failed", error); + } +} +``` + +## What To Expect + +- `executedWith` tells you which runtime handled the command +- `data` contains the success payload for that command +- command-specific failures are thrown, so use `try/catch` +- some commands still require backend verification even after a successful response, such as `walletAuth` and `pay` + +## Event Subscriptions + +For new 2.x command integrations, prefer `await MiniKit.()`. diff --git a/mini-apps/quick-start/testing.mdx b/mini-apps/quick-start/testing.mdx new file mode 100644 index 0000000..3ba756c --- /dev/null +++ b/mini-apps/quick-start/testing.mdx @@ -0,0 +1,42 @@ +--- +title: "Testing your mini app" +description: "Enter your App ID and scan the generated QR code to test your mini app." +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +import { QRCodeGenerator } from "/snippets/qr-code-generator.jsx"; + +# Testing + +### Testing your mini app + +Enter your app id in the text box below and scan the QR code generated with your phone's camera. +Your app id is in the developer portal in the format `app_xxxxxxxxxx`. + +{/* + The QR section below is wrapped in (rendered on the + web, excluded from the copy-page/.md export) with a plain-markdown twin in + (excluded from the web, rendered in the export). + The snippet import is safe ONLY because the usage sits inside the for="humans" + block: Mintlify inlines a snippet's full source into the export when the + component appears in exported content, and drops it when all usages are hidden + (verified on a preview deploy). Keep the twin in sync when editing. +*/} + + + + + + + To test on a phone, open `https://worldcoin.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. + 3. Confirm the prompt in World App. + + +### Tips + +1. You can use [Ngrok](https://ngrok.com/), [zrok](https://zrok.io/) or [tunnelmole](https://tunnelmole.com/) for local testing. +2. [Eruda](https://github.com/liriliri/eruda) is helpful for showing logs on mobile. diff --git a/mini-apps/reference/address-book.mdx b/mini-apps/reference/address-book.mdx new file mode 100644 index 0000000..854834e --- /dev/null +++ b/mini-apps/reference/address-book.mdx @@ -0,0 +1,32 @@ +--- +title: Address Book +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +The Address Book is a contract that stores verified World ID addresses. You can check if a user's address and associated ENS name (if available) is Orb verified using the `getIsUserVerified` helper function. + +## Considerations + +- Default RPC is `https://worldchain-mainnet.g.alchemy.com/public` +- Contract Address is [0x57b930D551e677CC36e2fA036Ae2fe8FdaE0330D](https://worldscan.org/address/0x57b930D551e677CC36e2fA036Ae2fe8FdaE0330D#readContract). + +## Implementation + +The helper function connects to the World Chain and checks if a given wallet address is verified by querying the Address Book contract. + +## Example Usage +```typescript +import { getIsUserVerified } from "@worldcoin/minikit-js/address-book" + +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 + + +## React Bindings + +For React applications, we provide a hook `useIsUserVerified` that handles the verification check and loading state. diff --git a/mini-apps/reference/credit-api.mdx b/mini-apps/reference/credit-api.mdx new file mode 100644 index 0000000..1002296 --- /dev/null +++ b/mini-apps/reference/credit-api.mdx @@ -0,0 +1,91 @@ +--- +title: Credit Scoring API +description: Real-time creditworthiness for World accounts. Look up by wallet address or World username to get borrower state and a credit score. +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +World proves there's a real human behind an account. The Credit API tells you whether you can trust them. It provides real-time creditworthiness data for [over half a million](https://credit.cash/analytics) World accounts that have borrowed on [Credit](https://world.org/ecosystem/app_ebdd8475db3238254fca5b25ccba266a). This lets you focus your app and incentives on trustworthy users who are less likely to game or farm your system. + +This API derives a user’s credit score solely from their on-chain behavior on Credit, using data sourced directly from smart contract events. + +## How to use it + +Send a `GET` request to: + +```bash +https://credit.cash/api/borrower/[identifier] +``` + +where `[identifier]` is either the user's wallet address or [World username](/mini-apps/reference/usernames). + +The response is a JSON object with two keys: + +- `state`: the user's status on Credit +- `score`: the user's credit score (integer) + +### User state + +`state` is one of: + +- `INACTIVE`: Borrower has no outstanding loan. They could be a new user or never borrowed. +- `ACTIVE`: Borrower has an outstanding loan that has not yet passed its overdue deadline. +- `DEFAULTED`: Borrower has an outstanding loan that is past due and not fully repaid. + +### Credit score + +The `score` is an integer that: + +- Starts at 0 for new users +- Increases as users successfully repay loans and build a repayment track record + +Use these bands to categorize borrowers: + +| **Score range** | **Band** | **Description** | +| --------------- | -------- | -------------------------------------------- | +| 0 | New | No loan history or no loans timely repaid | +| 1-99 | Bronze | Early borrower with minimal history | +| 100-299 | Silver | Developing credit with some successful loans | +| 300-599 | Gold | Established borrower with solid history | +| 600-999 | Platinum | Excellent track record | +| 1000+ | Diamond | Elite borrower with extensive history | + +## Example + +### Query + +Get data for user with wallet address `0x764C890E7D96481cBEa64c64C0F9cFF34bFF2Ce7`: + +```bash +curl -s "https://credit.cash/api/borrower/0x764C890E7D96481cBEa64c64C0F9cFF34bFF2Ce7" +``` + +### Response + +```json +{ + "state": "INACTIVE", + "score": 1947 +} +``` + +## Tips + +- Use score thresholds to shape your rewards and incentives. For example, offer higher rewards to users with high credit scores, who are more likely to be high-quality, and lower rewards to users with low credit scores, who are more likely to engage in fraud. +- Treat `DEFAULTED` as a strong risk signal. These accounts are much more likely to have sold their World account or be banned for fraud, so exclude them from sensitive actions like valuable rewards, referrals, and high-impact features. +- Add extra scrutiny or manual review for low-score users, such as additional verification or smaller caps, instead of blocking them outright. This lets you onboard new but unproven accounts safely. + +## Error codes + +| **Status** | **Error** | **Description** | +| ---------- | --------------------------- | -------------------------------------- | +| 400 | Missing identifier | No wallet address or username provided | +| 404 | Username not found | World username could not be resolved | +| 500 | Failed to get borrower info | Internal server error | + +## Support + +For support, questions, or suggestions, contact via Telegram: + +- [Diego](http://t.me/antidiego) +- [Monchi](http://t.me/cairoeth) diff --git a/mini-apps/reference/microphone.mdx b/mini-apps/reference/microphone.mdx new file mode 100644 index 0000000..5ccc7cf --- /dev/null +++ b/mini-apps/reference/microphone.mdx @@ -0,0 +1,52 @@ +--- +title: Microphone +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + + +Microphone is only available from World App 2.8.85 and MiniKit 1.9.6. + + +Microphone uses the standard web api [Navigator.mediaDevices](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/mediaDevices). +These two conditions must be met in order to use the microphone: +- The user must grant permission to your mini app to use the microphone. +- The user must grant permission to World App to use the microphone. + +## Using the microphone +Request permission from the user to enable microphone for your mini app with the [request permission command](/mini-apps/commands/request-permission). +- If you receive a `world_app_permission_not_enabled` or `permission_disabled` error code, you must prompt the user to enable microphone for World App first. +This can be done by simply trying to start recording. +- The microphone will automatically be turned off if the user closes your mini app or World App. + +```tsx + const [isMicOn, setIsMicOn] = useState(false); + const [stream, setStream] = useState(null); + // ... + const toggleMicrophone = useCallback(async () => { + if (isMicOn) { + // Stop microphone access + if (stream) { + stream.getTracks().forEach((track) => track.stop()); + setStream(null); + } + setIsMicOn(false); + } else { + // Start microphone access + try { + const newStream = await navigator.mediaDevices.getUserMedia({ + audio: true, + }); + setStream(newStream); + setIsMicOn(true); + } catch (error) { + console.error('Error accessing microphone:', error); + } + } + }, [isMicOn, stream]); +``` + + +## Debugging +- If you receive a `DomException Error`, it's most likely because the user hasn't granted permission to your mini app to use the microphone. +- If the microphone turns off after a few seconds it's because the user has not granted permission to your mini app to use the microphone. diff --git a/mini-apps/reference/payment-methods.mdx b/mini-apps/reference/payment-methods.mdx new file mode 100644 index 0000000..1764ba8 --- /dev/null +++ b/mini-apps/reference/payment-methods.mdx @@ -0,0 +1,87 @@ +--- +title: Payment Methods +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Integrate Apple Pay and Google Pay into your mini-app + + + Both Apple Pay and Google Pay run directly in the WebView environment of your + mini-app, using their respective JavaScript APIs. + + +## Environment Setup + +First, ensure your mini-app has a secure HTTPS environment, as both Apple Pay and Google Pay require it. + + + + For Apple Pay, you must: - Register for an Apple Developer account - + Configure your merchant ID - Register your domain with Apple - Set up + merchant validation on your server [Complete setup guide on Apple's + website](https://applepaydemo.apple.com/) + + + + For Google Pay, you must: - Set up a Google Pay merchant account - Configure + your Google Pay API in the Google Pay Business Console - Obtain your + merchant ID [Complete setup guide on Google's + website](https://developers.google.com/pay/api/web/guides/resources/demos) + + + +### Testing + +Test your integration thoroughly using test cards and sandbox environments. + + + + **Development Environment:** - Use Apple Pay's sandbox environment - Test + with Apple's test cards **Test Cards:** - You can use any valid card number + in the sandbox - No actual charges will be processed **Testing Checklist:** + - Verify merchant validation works - Test on both iOS devices and Safari on + macOS - Check error handling - Verify payment completion flows + + Apple Pay integration demo + + + + **Development Environment:** - Use Google Pay's TEST environment - Test with + Google's test cards **Test Cards:** - Use test PAN: 4111 1111 1111 1111 - + Expiration: Any future date - CVV: Any 3 digits **Testing Checklist:** - + Verify button displays correctly - Test on Android devices - Check error + handling - Verify payment completion flows + Google Pay integration example + Google Pay integration example + + + +## Limitations and Considerations + +- **Platform Restrictions**: Apple Pay only works on iOS/macOS devices with Safari, while Google Pay is primarily for Android +- **Device Support**: Users must have devices that support these payment methods +- **WebView Context**: Some features might behave differently in the mini-app WebView compared to a standard browser + + + For Apple Pay merchant validation, your server needs to be accessible via + HTTPS and properly configured with Apple's developer portal. + + +## Additional Resources + +- [Apple Pay Demo and Documentation](https://applepaydemo.apple.com/) +- [Google Pay API Documentation](https://developers.google.com/pay/api/web/guides/resources/demos) +- [Apple Pay Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/apple-pay/overview/) +- [Google Pay Web Integration Guide](https://developers.google.com/pay/api/web/guides/tutorial) diff --git a/mini-apps/reference/status-page.mdx b/mini-apps/reference/status-page.mdx new file mode 100644 index 0000000..bd23ad6 --- /dev/null +++ b/mini-apps/reference/status-page.mdx @@ -0,0 +1,127 @@ +--- +title: Status Page +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +You can check the current status of World services at [status.world.org](https://status.world.org). + + +The Networks section is not up to date yet. + + +For transactions, status is determined by: + +- **Disruption**: Transactions taking longer than 45 seconds +- **Outage**: Transactions taking longer than 5 minutes + +## Get Status + + + https://status.worldcoin.org/api/services + + +This endpoint returns the current status of all World services. + +### Query Params + + + Include `logs=true` to get historical status logs for each service. + + + +```bash cURL +curl -X GET "https://status.worldcoin.org/api/services?logs=true" +``` + +```javascript +fetch('https://status.worldcoin.org/api/services?logs=true') +``` + + +### Response + + + Array of service status objects, each containing: + + + + Service name (e.g., "Mini Apps", "World ID Verifications") + + + + Service identifier + + + + Description of the service + + + + Category the service belongs to (e.g., "mini-apps", "world-id", "finance") + + + + Current service status: "ok", "warning", or "error" + + + + Historical status changes, containing: + - `datetime`: Unix timestamp + - `status`: Status at that time + - `name`: Event name + - `description`: Event description + - `downtime`: Duration in seconds (if applicable) + + + + Uptime percentages for different time periods: + - `1`: Last 24 hours + - `7`: Last 7 days + - `30`: Last 30 days + - `90`: Last 90 days + + + +```json +{ + "services": [ + { + "name": "Crypto Transactions", + "id": "crypto-transactions", + "description": "", + "categoryId": "mini-apps", + "status": "ok", + "logs": [ + { + "datetime": 1739546233, + "status": "ok", + "name": "Running again", + "description": "Service outage" + } + ], + "uptimeRatio": { + "1": 99.7, + "7": 100, + "30": 100, + "90": 100 + } + } + ], + "categories": [ + { + "id": "mini-apps", + "name": "Mini Apps", + "status": "ok" + } + ], + "uptimeRatio": { + "1": 98, + "7": 98.7, + "30": 99.6, + "90": 99.9 + }, + "status": "ok" +} +``` + diff --git a/mini-apps/reference/usernames.mdx b/mini-apps/reference/usernames.mdx new file mode 100644 index 0000000..e36e64b --- /dev/null +++ b/mini-apps/reference/usernames.mdx @@ -0,0 +1,28 @@ +--- +title: Usernames +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +## Introduction + +Usernames are ENS-compatible identifiers for every World App user, ensuring consistency and easy recognition. +For example, when displaying transaction history, show the username instead of the wallet address to make it more user-friendly and private. + +The usernames service is public, docs can be found [here](https://usernames.worldcoin.org/docs). This will be mostly useful for more advanced use cases. + +## How to get it + +To get the user's username you can either complete Wallet Auth and access the username/profile picture url from MiniKit directly: + +```tsx +const username = MiniKit.user.username +``` + +Or you can request it manually, using the `getUserByAddress` method on MiniKit: + +```tsx +const worldIdUser = await MiniKit.getUserByAddress(userAddress) +``` + +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 new file mode 100644 index 0000000..8ed1158 --- /dev/null +++ b/mini-apps/sharing/add-money-qa.mdx @@ -0,0 +1,65 @@ +--- +title: "Add Money" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Add money to your World Wallet directly from exchanges like Binance and Coinbase. Deposit, withdraw, and swap tokens across multiple exchanges and chains effortlessly. + +
+
+

Parameters

+ + Unique ID for the Add Money mini app. + + + URL-encoded path to the bridge interface (typically `%2Fbridge`). + + + Address of the recipient World Wallet user. + + + Token contract address to deposit (supports USDC or WLD). + + + USD amount to deposit. If omitted, the user selects the amount in-app. + + + App ID of the source app for quick navigation back. + + + Name of the source app displayed in the "Go Back" button. + + + Path within the source app to deeplink back to (URL-encoded). + +
+
+ Add Money Screen +
+
+ +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} +``` diff --git a/mini-apps/sharing/dna-qa.mdx b/mini-apps/sharing/dna-qa.mdx new file mode 100644 index 0000000..7b223cd --- /dev/null +++ b/mini-apps/sharing/dna-qa.mdx @@ -0,0 +1,146 @@ +--- +title: "DNA Send & Swap" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Generate deep links to the DNA app for quick actions like Swap and Send. + +[DNA](https://worldcoin.org/ecosystem/app_8e407cfbae7ae51c19b07faff837aeeb) now supports a **Quick Action** to deeplink directly +into the wallet interface, allowing users to perform specific actions like sending tokens or swapping assets with predefined parameters. + +
+
+

Parameters

+ + Supports deep linking to the `swap` and `send` tabs. + + + The contract address of the token being sent (`fromToken`). + + + The contract address of the token being received (`toToken`). This is used + in **swap** actions. + + + The recipient's wallet address or username for sending tokens. + + + The amount of the `fromToken` to be sent, specified in its **base unit** . + + + The application ID of the source app initiating the deeplink. + + + A deeplink path from the source application, which will be + **URL-encoded**. + +
+ + + Send Screen + + + Swap Screen + + +
+ +## Helper Function + +```tsx +const DNA_APP_ID = "app_8e407cfbae7ae51c19b07faff837aeeb"; + +function getDNADeeplinkUrl({ + tab, + fromToken, + toToken, + recipientAddress, + amount, + sourceAppId, + sourceDeeplinkPath, +}: { + tab: "swap" | "send"; + fromToken?: string; + toToken?: string; + recipientAddress?: string; + amount?: string; + sourceAppId?: string; + sourceDeeplinkPath?: string; +}) { + let path = `/wallet?tab=${tab}`; + + if (fromToken) { + path += `&fromToken=${fromToken}`; + if (amount) { + path += `&amount=${amount}`; + } + } + + if (toToken) { + path += `&toToken=${toToken}`; + } + + if (recipientAddress) { + path += `&recipientAddress=${recipientAddress}`; + } + + if (sourceAppId) { + path += `&sourceAppId=${sourceAppId}`; + } + + if (sourceDeeplinkPath) { + path += `&sourceDeeplinkPath=${encodeURIComponent(sourceDeeplinkPath)}`; + } + + const encodedPath = encodeURIComponent(path); + return `https://worldcoin.org/mini-app?app_id=${DNA_APP_ID}&path=${encodedPath}`; +} +``` + +## **Returns** + +A string representing the complete deeplink URL to the DNA application with the specified parameters. + +## **Example Usage** + +```typescript +const deeplinkUrl = getDNADeeplinkUrl({ + tab: "send", + fromToken: "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1", + toToken: "0x4200000000000000000000000000000000000006", + recipientAddress: "0xRecipientAddressHere", + amount: "1235", + sourceAppId: "app_a4f7f3e62c1de0b9490a5260cb390b56", + sourceDeeplinkPath: "/path", +}); +console.log(deeplinkUrl); +``` + +## **Generated Deeplink URL:** + +```bash +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** + +- Ensure that the **amount** is specified in the unit of the fromToken (e.g., wei for Ethereum-based tokens). +- The **sourceDeeplinkPath** is URL-encoded to ensure it is correctly interpreted when the deeplink is accessed. +- The **DNA_APP_ID** should be defined in your environment to match the application ID assigned to your DNA instance. +- If the tab is **Send**, it is necessary/recommended to provide **fromToken**, **amount**, and **the recipient's address or username** _(toToken is not required)_. +- If the tab is **Swap**, it is necessary/recommended to provide **fromToken**, **toToken**, and **amount** _(in base unit)_. + +This function facilitates the creation of deeplink URLs that can be used to direct users seamlessly into specific actions within the DNA application, enhancing the user experience by pre-filling transaction details. diff --git a/mini-apps/sharing/earn-wld-qa.mdx b/mini-apps/sharing/earn-wld-qa.mdx new file mode 100644 index 0000000..97934ef --- /dev/null +++ b/mini-apps/sharing/earn-wld-qa.mdx @@ -0,0 +1,34 @@ +--- +title: "Earn WLD Pool" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Earn high yields with your WLD token. Put your WLD to work by adding liquidity to the markets and earning large rewards from other traders. +Earn WLD now supports a Quick Action to deeplink directly to their staking screen. + +
+
+

Parameters

+ + + Unique ID for the Earn WLD mini app. + + +
+
+ Liquidity Pool Screen +
+
+ +Url follows the schema below. Navigate there to use this Quick Action. + +``` +http://worldcoin.org/mini-app?app_id=app_b0d01dd8f2bdfbff06c9e123de487eb8 +``` diff --git a/mini-apps/sharing/quick-actions.mdx b/mini-apps/sharing/quick-actions.mdx new file mode 100644 index 0000000..52c8626 --- /dev/null +++ b/mini-apps/sharing/quick-actions.mdx @@ -0,0 +1,43 @@ +--- +title: "Link to your Mini App" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +### **What Are Quick Actions?** + +A Quick Action is a universal deeplink defined by a schema that navigates to a specific path inside your app. It's meant to be used between mini apps, enabling one app (App A) to use a feature or action of another app (App B) seamlessly. +In short, you should create a quick action to link to another mini app. + + +If a user clicks on your link and has World App installed, it will automatically open the mini app inside of World +App to the specified path. Otherwise, it will direct them to the app store and prompt them to install World App. + + +### **Benefits of Quick Actions** + +1. **Leverage Expertise**: Use other mini apps already working specific features. +2. **Resource & Time Optimization**: Focus on your apps core functionalities while outsourcing supplementary features. +3. **Community Growth**: Integrating another apps functionality opens opportunities for co-marketing. + +### **How to Make a Quick Action** + +1. **Create a Universal Link Schema**: Define a schema with a custom path that aligns with the functionality you want to provide. + +2. **Publish it in our docs for all devs to use**: Fill this [form](https://forms.gle/UBcKMrnxtyxqX4dq6) for our team to test and publish your quick action on this page. + +Urls will follow the schema below. + +``` +https://world.org/mini-app?app_id={app_id}&path={path} +``` + +### Parameters + + + The `app_id` corresponding to your mini app. + + + + Should be the url encoded path where you want to link to inside of your mini app + diff --git a/mini-apps/sharing/sage-qa.mdx b/mini-apps/sharing/sage-qa.mdx new file mode 100644 index 0000000..0fe8344 --- /dev/null +++ b/mini-apps/sharing/sage-qa.mdx @@ -0,0 +1,208 @@ +--- +title: "Sage Support" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +[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. + +## Key Features + +- **Seamless Integration**: Easily embed Sage chats within any World Mini App. +- **Custom Context Parameters**: Tailor Sage's responses based on Mini App specific data. +- **Logs and Analytics**: Track and understand what types of questions your users are asking. + +
+ Sage Developer Dashboard Analytics + Sage Developer Dashboard Settings +
+ +--- + +## Getting Started + +### 1. Create a Sage Developer project + +Go to the [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) and sign in. Create a new project. + +### 2. Customizing Sage to fit your app + +With a project created, head over to the project's settings, this page is accessible by going into: **Apps > Your App > Settings**. (Navigation is available through the menu bar on the top right or the top left breadcrumbs) + +In this page you can find parameters to fine tune Sage to best interact with your users in the context of your app, a comprehensive description of these parameters is present [below](#app-settings). + +### 3. Integrating Sage Support in your app + +The final step to integrating Sage Support into your React application is to install and make use of the `SageSupport` component. + +Add the npm package with the command: + +```bash +npm install sage-support +``` + +Once the dependency installed, the simplest way to integrate Sage Support is: + +```tsx +import { SageSupport } from "sage-support"; + +; +``` + +Where `YOUR_APP_ID` is the App ID (number) you can find in the [Sage Developer Dashboard](https://dev-dashboard-gamma.vercel.app) (top right). + +More configuration options for the component are available [below](#sagesupport-component). + +### 4. Sage Support Link + +Generate your app's Support Link using the [Support Link wizard](#support-link-generator) and use it as the Support URL under **Worldcoin Developer > Mini App > Configuration > App Store > Support > Link**. + +### Analytics + +Once you've started using Sage Support in your Mini App, analytics are also available in the [Sage Developer Dashboard](https://dev-dashboard-gamma.vercel.app), allowing you to identify the usage of Sage Support in your app and understand how users are using it. You can find more information about analytics [below](#app-analytics). + + + This is all you need to get Sage Support working. If you wish to customize it + further, or better understand the various parameters continue reading below. + + +--- + +## App Settings + +Located at **Apps > Your App > Settings**, the Settings page allows you to fine tune the Sage Support assistant to meet the needs and context of your Mini App. + +### `App Name` + +This name only lives in the [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app) universe and serves the purpose of differentiating between multiple Sage Support integrations, it's recommended that this field is set accordingly. + +### `App ID` + +Uniquely identifies your Sage Developer App and is used to link your `SageSupport` component to the [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app). This field is automatically generated and cannot be changed. + +### `Introduction Message` + +Defines the first message to be sent by the Sage assistant once a new chat is created/open by a user. This field is also capable of encompassing a custom variable (`%USER%`) which Sage will replace by the user's username. + +### `App Description` + +Provide a solid description about your app and what it focuses on, this field is processed by Sage as an additional context parameter, and is useful to keep the assistant behavior and subject within the landscape of your Mini App. + +--- + +## App Analytics + +Located at **Apps > Your App > Analytics**, the Analytics page allows you to view statistics and analytic data about the usage of Sage Support within your Mini App. + +### `Messages` + +See how many messages were sent to Sage Support chats from your Mini App in each of the last 7 days. + +### `Users` + +See how many users interacted with Sage Support chats from your Mini App in each of the last 7 days. + +### `Keywords` + +Currently not available. + +### `Next Step` + +Currently not available. + +### `Logs` + +See how users chat with Sage Support inside your app, this data is shown anonymously and provides the developer with a more in-depth tool to explore the interactions between users and Sage Support. + +--- + +## `SageSupport` Component + +### Installation + +To use the Sage Support React component you must first install it using npm (or your package manager of choice). + +```bash +npm install sage-support +``` + +### Basic Usage + +From the get go you can use Sage Support by using the basic implementation, the only mandatory parameter to be passed to the component is the `projectId`. This ID (App ID) is retrieve from the Settings page in the [Sage Developer dashboard](https://dev-dashboard-gamma.vercel.app). + +```tsx +import { SageSupport } from "sage-support"; + +; +``` + +### Props & Personalisation + +Other props allow you to customize both the aesthetic and behavior of the Sage Support Chat. + + + Links your component to your Sage Developer project, used for analytics and + behavior. + + + Allows you to make use of the return button on the top left of the Sage + Support chat window. The value to be passed to this prop is a [quick action + URL](/mini-apps/sharing/quick-actions). + + + Allows you to pass custom CSS classes to affect the fallback default button. + + + Allows you to pass a custom React element to replace the default button. + + +--- + +## Support Link + +You can also integrate Sage Support directly into your World Mini App by using a Support Link. This allows your users to access Sage's AI-powered support without requiring you to install any additional packages or components. + +The Support Link is a quick action URL that opens the Sage Support chat interface directly within the World App. When users click on this link, they'll be able to ask questions and get support related to your Mini App. + +This link is particularly useful for developers to use Sage as their Mini App's native Support link, available under **Worldcoin Developer > Mini App > Configuration > App Store > Support > Link** + +### Base URL & Parameters + +The base URL to access a support chat is: + +`https://worldcoin.org/mini-app?app_id=app_5dee2f19cd6eef599eb6ab275a0a7523&path=/support-chat` + +This support link, akin to the [`SageSupport` component](#sagesupport-component), accepts and requires some parameters to be passed, in this case URL Query Parameters, for the Support Chat to work like expected. + + + Links your Support Link to your Sage Developer project, used for analytics and behavior. + + + Allows you to make use of the return button on the top left of the Sage Support chat window by passing a Mini App quick action URI/URL. Due to some encoding functionalities and implementation caveats, the value to be passed to this parameter has to be formatted accordingly beforehand: + + 1. The URL must be submitted to a [UTF-8 encoding](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent). + + 2. All ampersand characters (such as the one in `&path`) have to be replaced with the sequence `(amps)`. + + To make this process easier, you can make use of the generator below or inside the [Sage Developer Dashboard](https://dev-dashboard-gamma.vercel.app). Any URIs outside of the `worldcoin.org/mini-app` space are not allowed. + + + +### Support Link Generator + + + The Support Link Generator component would be inserted here in the actual + implementation. + diff --git a/mini-apps/sharing/swap-qa.mdx b/mini-apps/sharing/swap-qa.mdx new file mode 100644 index 0000000..0a4bbcd --- /dev/null +++ b/mini-apps/sharing/swap-qa.mdx @@ -0,0 +1,94 @@ +--- +title: "Swap" +"og:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/toolsforhumanity/docs/main/images/docs/docs-meta.png" +--- + +Use this Quick Action to deep link into the Swap interface with prefilled parameters. + +
+
+

Parameters

+ + Target app id for the Swap quick action:{" "} + app_6c5c5717c77abe83be8814c032c3a6f9. + + + Path inside the target app. Use '/' for Swap. + + + Token address for the "from" token. + + + Token address for the "to" token. + + + Amount of the from token in base token units. + + + app_id of the app that uses this quick action. + + + Name of the app that uses this quick action. + +
+
+ Swap Screen +
+
+ +### Helper function + +```ts +export interface ActionLinkProps { + targetAppId: string; // app_6c5c5717c77abe83be8814c032c3a6f9 for Swap quick action + path: string; // '/' for Swap quick action + params: Record; +} + +const MAIN_ACTION_URL = "https://worldcoin.org/mini-app"; + +export function getActionLink({ targetAppId, path, params }: ActionLinkProps) { + let fullPath = path; + + if (Object.keys(params).length > 0) { + fullPath += `?${new URLSearchParams(params)}`; + } + + const queryString = `${MAIN_ACTION_URL}?app_id=${targetAppId}&path=${encodeURIComponent(fullPath)}`; + + return queryString; +} + +// Example usage +const link = getActionLink({ + targetAppId: "app_6c5c5717c77abe83be8814c032c3a6f9", + path: "/", + params: { + fromToken: "0x2cFc85d8E48F8EAB294be644d9E25C3030863003", + toToken: "0x4200000000000000000000000000000000000006", + amount: "1234500", + sourceAppId: "app_source1234567890abcdef", + sourceAppName: "My App", + }, +}); +console.log(link); +``` + +### Example link + +``` +https://worldcoin.org/mini-app?app_id=app_6c5c5717c77abe83be8814c032c3a6f9&path=%2F%3FfromToken%3D0x2cFc85d8E48F8EAB294be644d9E25C3030863003 +``` diff --git a/mini-apps/sharing/world-chat-qa.mdx b/mini-apps/sharing/world-chat-qa.mdx new file mode 100644 index 0000000..2c9630c --- /dev/null +++ b/mini-apps/sharing/world-chat-qa.mdx @@ -0,0 +1,147 @@ +--- +title: "World Chat" +--- + +World Chat is a messaging platform in the World App ecosystem. +Quick Actions let you link to specific chat features. + +Url follows the schema below. + +``` +https://worldcoin.org/mini-app?app_id=app_e293fcd0565f45ca296aa317212d8741 +``` + +
+
+

Parameters

+ + The username of the recipient you want to chat with. You can resolve a + username using the user's [wallet + address](/mini-apps/reference/address-book). + + + Predefined message text to include in the draft chat. + + + When included, opens a draft chat with the send payment option + pre-selected. You can optionally specify an amount by using `pay=amount` + where amount is the numeric value in USDC. + + + When included, opens a draft chat with the payment request option + pre-selected. You can optionally specify an amount by using + `request=amount` where amount is the numeric value in USDC. + +
+
+ World Chat Screen +
+
+ +## Helper function + +```tsx +const WORLD_CHAT_APP_ID = "app_e293fcd0565f45ca296aa317212d8741"; + +function getWorldChatDeeplinkUrl({ + username, + message, + pay, + request, +}: { + username: string; + message?: string; + pay?: string | number; + request?: string | number; +}) { + let path = `/${encodeURIComponent(username)}/draft`; + + if (message) { + path += `?message=${encodeURIComponent(message)}`; + } else if (pay !== undefined) { + if (pay === "true" || pay === true) { + path += `?pay`; + } else { + path += `?pay=${encodeURIComponent(String(pay))}`; // Pay with amount + } + } else if (request !== undefined) { + if (request === "true" || request === true) { + path += `?request`; + } else { + path += `?request=${encodeURIComponent(String(request))}`; // Request with amount + } + } + + const encodedPath = encodeURIComponent(path); + return `https://worldcoin.org/mini-app?app_id=${WORLD_CHAT_APP_ID}&path=${encodedPath}`; +} + +// Create a chat with predefined message +console.log( + getWorldChatDeeplinkUrl({ + username: "johndoe", + message: "Hello from my mini app!", + }) +); + +// Create a chat with send payment option +console.log( + getWorldChatDeeplinkUrl({ + username: "johndoe", + pay: "true", + }) +); + +// Create a chat with send payment option and amount +console.log( + getWorldChatDeeplinkUrl({ + username: "johndoe", + pay: 5.25, + }) +); + +// Create a chat with payment request option +console.log( + getWorldChatDeeplinkUrl({ + username: "johndoe", + request: "true", + }) +); + +// Create a chat with payment request option and amount +console.log( + getWorldChatDeeplinkUrl({ + username: "johndoe", + request: 10, + }) +); +``` + +### Example output links + +``` +// Message draft +https://worldcoin.org/mini-app?app_id=app_e293fcd0565f45ca296aa317212d8741&path=%2Fjohndoe%2Fdraft%3Fmessage%3DHello + +// Payment draft with amount +https://worldcoin.org/mini-app?app_id=app_e293fcd0565f45ca296aa317212d8741&path=%2Fjohndoe%2Fdraft%3Fpay%3D5.25 + +// Request draft with amount +https://worldcoin.org/mini-app?app_id=app_e293fcd0565f45ca296aa317212d8741&path=%2Fjohndoe%2Fdraft%3Frequest%3D10 +``` + +### Appendix + +**Caveats/Warnings** + +- The username must be a valid World ID username. +- Only one quick action type (message, pay, or request) can be used at a time. +- If the recipient does not exist, the app will show an appropriate error message. +- Only USDC & WLD are supported for payments. +- Currency amounts should be specified as decimal numbers (e.g., 5.25 for 5.25 USDC). diff --git a/snippets/qr-code-generator.jsx b/snippets/qr-code-generator.jsx new file mode 100644 index 0000000..e611287 --- /dev/null +++ b/snippets/qr-code-generator.jsx @@ -0,0 +1,97 @@ +// /snippets/qr-code-generator.jsx +import { useMemo, useState } from "react"; + +/** + * Simple QR generator for Mintlify (no external NPM deps). + * Renders an that points to a public QR service. + * If you prefer zero external calls, swap the for a + * and inline a tiny QR encoder instead. + */ +export const QRCodeGenerator = (props) => { + const { + appId: initialAppId = "", + hideInput = false, + baseUrl = "https://worldcoin.org/mini-app", + hideDetails = false, + } = props || {}; + const [appId, setAppId] = useState(initialAppId); + + const trimmed = useMemo(() => appId.trim(), [appId]); + const isValid = useMemo(() => /^app_[a-f0-9]+$/.test(trimmed), [trimmed]); + + // Encode the mini-app URL with the app_id; domain is configurable via baseUrl + const payload = useMemo(() => { + if (!trimmed || !isValid) return ""; + return `${baseUrl}?app_id=${trimmed}`; + }, [trimmed, isValid, baseUrl]); + + const qrSrc = useMemo(() => { + const size = "200x200"; + const data = encodeURIComponent(payload); + return `https://api.qrserver.com/v1/create-qr-code/?size=${size}&data=${data}`; + }, [payload]); + + const containerGridClass = hideDetails + ? "" + : "grid gap-4 md:grid-cols-[200px_1fr] items-start"; + + return ( +
+ {!hideInput && ( + + )} + + {trimmed && !isValid && ( +

+ Invalid App Id. Eg. app_xxxxxxxxxxx +

+ )} + +
+
+ {isValid && payload ? ( + {`QR + ) : ( +
+ {hideInput + ? "Provide an App ID via props" + : "Enter a valid App ID"} +
+ )} +
+ + {!hideDetails && ( +
+
+ Encoded value: + + {payload || "(empty)"} + +
+
    +
  1. Enter your App ID {hideInput ? "via props" : "above"}.
  2. +
  3. Scan the QR with your phone’s camera.
  4. +
  5. Confirm the prompt in World App.
  6. +
+
+ )} +
+
+ ); +};