quickstart

First OTP in 5 minutes

From zero to a verified session with a signed JWT. No payment, no KYC — the test environment is fully simulated.

1. Create an app

Dashboard → New app → you get two environments: test (free, simulated OTP) and live (real sends, credits). Keys:

your keys
pk_test_xxx   — publishable, safe in browsers/mobile apps
sk_test_xxx   — secret, server-side only
whsec_xxx     — webhook signing secret
(pk_live_/sk_live_ on the live environment)

2. Send an OTP

curl
curl https://api.nexauth.dev/v1/otp/send \
  -H "Authorization: Bearer pk_test_xxx" \
  -d '{"channel":"whatsapp","identifier":"+919999900001"}'

→ {"otp_id":"…","status":"sent","dev_code":"4570437"}
  test env returns dev_code — no message is sent

3. Verify → session

curl
curl https://api.nexauth.dev/v1/otp/verify \
  -H "Authorization: Bearer pk_test_xxx" \
  -d '{"channel":"whatsapp","identifier":"+919999900001","code":"4570437"}'

→ {"access_token":"eyJ…","refresh_token":"…","expires_in":900,"new_user":true}

4. Verify the JWT anywhere — offline

Tokens are RS256, signed per-tenant. Your backend verifies offline against our JWKS — no network call, no secret sharing:

go
jwks := nexauth.NewJWKS("https://api.nexauth.dev/v1/jwks.json")
claims, err := jwks.Verify(accessToken)
// claims: sub, app_id, env, roles[]

5. Plug the SDK instead

typescript
import NexAuth from "@nexauth/sdk";
const auth = new NexAuth({ publishableKey: "pk_test_xxx" });

await auth.otp.send({ channel: "whatsapp", to: phone });
const session = await auth.otp.verify({ channel: "whatsapp", to: phone, code });

What just happened?

Next