Skip to main content
Add “Sign in with PeridotID” to any web app in three steps. Your users authenticate against PeridotID production — no local setup, no secrets to manage.

1. Register your app

Call POST /v1/apps with your PeridotID session (cookie) to get a public client_id. List every URL PeridotID may return users to — codes are only issued to these:
Then add your websites under Allowed origins (bare https://… origins, no paths). Pass the exact return URL in code — its origin must be listed: Your redirect URI origins are automatically allowed for browser API calls (within ~60s of registering, no redeploy needed). Local development needs nothing at all: http://localhost, http://127.0.0.1 and http://[::1] (any port, any path) are always allowed — arbitrary production hosts are never accepted without registration (an open redirect would let anyone steal users’ login codes).

2. Open the login popup

Auth opens a centered popup on the PeridotID origin (Google, plus the PID picker/claim if the visitor has no PeridotID yet). It stays open through the whole flow and closes only once the user allows (your page receives the one-time pid_code via window.opener) or denies/cancels. If the popup is blocked, the SDK falls back to a new tab automatically; peridot.auth.loginTab() is available if you prefer a tab outright. You never handle a password and nothing runs in your DOM. Passkeys run on the PeridotID origin (browsers bind WebAuthn to our domain); Google uses the redirect round-trip. No API calls happen before login, so there is nothing CORS-related to configure.
First sign-in creates a PID. If the visitor has no PeridotID yet, the window shows the emission-free Create your PID step (the handle is permanent and user-chosen). Login only resolves once a PID exists and the user allows. popupBaseUrl is reused as the auth host; approval popups (a smaller window on the PeridotID origin) are used for confirmations like transfers and payments.
In React, wrap the same two calls in your own button + hook — there is no provider component to install.

Who owns what: auth vs session

We authenticate the human, you sessionize your user. PeridotID proves “this browser is rani@pid” and hands you proof (pid_code → exchange → { pid, profile, accessToken }). Everything session-shaped is yours: your cookie, your expiry, your logout route, your users table keyed by pid. Our session cookies (pid_access/pid_refresh) never leave our origin — no third-party app can or should read them. One rule covers most mistakes: keep the accessToken server-side. It is a read-only bearer (identity, profile, balance, ledger — never writes), and anything in browser JS or localStorage is stealable via XSS. Exchange in the browser while prototyping if you like; production backends exchange server-side (step 3) and hand the browser only their own httpOnly cookie.

3. Handle the login

After login the tab returns a one-time pid_code (posted to your opener; a full-page redirect_uri fallback is also supported). Already signed in? A consent screen appears first — who you’re continuing as, with Allow, a different account, or Deny — and only Allow issues the code. Denying returns with ?error=access_denied. For production, exchange server-side instead of calling peridot.auth.exchange in the browser: verify the code against PeridotID, store the bearer server-side, then mint your app’s own session:
Know the limits before promising anything to your users:
  • The bearer is read-only (scope: "read"): balance/ledger/identity/profile are reachable, transfers and writes always return 403.
  • It lasts 30 days, non-refreshable — afterwards the user re-clicks login (one tap if their PeridotID session is alive). Never log the bearer.
  • There is no single-token revoke: a leaked bearer stays valid until expiry unless the whole identity is deactivated. The read-only scope is the containment — the reason this pattern is blessed for reads only.
  • Verify-or-cache is your call: live-verify every request (fail-closed when we’re down, deactivation honored immediately) or cache identity up to ~15 min per route — never longer.
  • Logout doesn’t propagate: our sign-out ends our session, not yours — delete your own cookie server-side on your logout route.
  • Don’t confuse it with the machine token: the user bearer reads their data; POST /v1/auth/token reads your app’s data (escrow balance).

Notes

  • Passkey login always runs on a PeridotID-hosted page (browsers bind WebAuthn to our domain) and returns with pid_code like Google does.
  • Codes are single-use, expire in ~5 minutes, and are bound to your client_id.
  • No framework? The snippet in step 2 is already framework-free: loginPopup (popup, tab fallback) or openLoginTab/openLoginPopup, plus loginWithPasskey({ clientId, returnTo }) and exchange(code, clientId).

Verified partners

Admins can mark your app as a verified partner (in the dashboard). Verified apps show a ✓ badge on the login consent screen — and your users skip the PeridotID fiat fee on top-ups, sends, and withdrawals (0%). Your own app fee, DOKU provider fees, and on-chain fees still apply. Interested? Talk to us — verification is granted, never self-issued.

Next: money

Once users can sign in, you can fund their balance, hold escrow on your app’s account, and charge your own fee:
  • Fiat & payments — top-up, balance, sends, escrow.
  • Apps & workspace — manage your app, set per-app fees, read the escrow balance with a machine token.