Docs

MoonX, PayBox, and x402 for people who still have email.

MoonX

moonx is how a person signs in and gets a wallet without becoming a part-time custody officer. email/oauth in. passkey-gated key material. moonx never holds the raw keys. if that sentence confuses you, stop and read it again.

mental model

  • identity: email OTP or google/apple oauth via useMoonX().start()
  • wallets: explicit create; passkey encrypts key material; presence tokens for step-up
  • sessions: moonx issues tokens; your backend verifies against JWKS if you need an app session

package: @moon-x/react-sdk

when to use

building a product for humans. escrow, tip links, sign walls, anything where "connect wallet" is a conversion killer.

when not to use alone

agents that need to pay on a schedule. that is paybox. protocol pay-per-request without accounts is x402.

passkeys (short)

webauthn is the browser API. a passkey is the credential. moonx uses passkey step-up so sensitive wallet ops stay device-backed / MPC-gated. the same breaker is what paybox uses when an agent spend exceeds autonomous limits.

dashboard Wallets → Relying Party ID (webauthn.rp_id) must be set once to your apex (e.g. k4d.io). change it later and every existing passkey is orphaned. PSL hosts like vercel.app are not valid RP IDs.

dashboard map

dashboard.moonx-dev.com → app: Overview · Users · Developer (keys + JWKS) · Authentication · Wallets · Origins · Compliance (ToS / captcha) · Branding · Integrations (WalletConnect / Swaps / MoonPay) · Settings.

gotchas

  • publishable key in the browser. secret key never in the browser. this is not a style preference.
  • dashboard allowed origins must match every host you deploy (exact origin, or a single-label preview wildcard).
  • wallet ops need passkey assertions + a configured RP ID. do not "simplify" that away.
  • email OTP alone is a weak signup bar — the same public SDK flow a browser uses can be scripted. enable Compliance → Captcha when open signup matters; passkey enrollment binds a human device.
  • copy the per-app JWKS URL from Developer for backend verification (@moon-x/node-sdk), not a guessed global path.

sample

import { MoonXProvider, useMoonX } from "@moon-x/react-sdk";
import { useCreateWallet } from "@moon-x/react-sdk/ethereum";

function Login() {
  const { start } = useMoonX();
  return <button onClick={() => start()}>sign in</button>;
}

official wins

normative docs: moonx getting started · backend verification

recipe: passkey-app · skill: skills