# PeridotID - [Overview](https://docs.peridot.id/index.md): What PeridotID is and what it provides - [Getting started](https://docs.peridot.id/getting-started.md): Base URLs, versioning, and your first request - [Environments (production & sandbox)](https://docs.peridot.id/environments.md): Production and sandbox stacks — real money vs DOKU sandbox - [Self-hosting](https://docs.peridot.id/self-hosting.md): Run the PeridotID API yourself - [Authentication](https://docs.peridot.id/authentication.md): Google sign-in, cookies, refresh rotation, and logout - [JavaScript SDK](https://docs.peridot.id/sdk.md): The official PeridotID SDK for the browser - [Fiat & payments](https://docs.peridot.id/fiat.md): Top up, hold, and send IDR balances with the PeridotID fiat ledger - [Apps & workspace](https://docs.peridot.id/apps.md): Register an app, manage its settings and fees, and read its escrow - [Sign in with PeridotID](https://docs.peridot.id/sign-in-with-peridot-id.md): Let users sign into your app with their PeridotID (Google or passkey) - [Embed the wallet](https://docs.peridot.id/embed-wallet.md): Render the PeridotID wallet inside your React app; sign-in and money actions open the PeridotID popup - [Errors](https://docs.peridot.id/errors.md): Error format, status codes, and rate limits - [API Reference](https://docs.peridot.id/api/overview.md): Every endpoint of the PeridotID API - [Start Google login](https://docs.peridot.id/api-reference/auth/start-google-login.md): Returns the URL a client should navigate to in order to begin Google OAuth. The URL points to `GET /auth/google`, which redirects the browser to Google's consent screen. After the user consents, the browser is redirected back through `/auth/google/callback` and session cookies are set. - [Check PID handle availability](https://docs.peridot.id/api-reference/auth/check-pid-handle-availability.md): Returns whether `handle` is free (`@pid`). Used by the onboarding handle picker before starting Google sign-up. Handles are lowercase `^[a-z0-9_]{3,20}$`; the full PID is `@pid` — permanent, never changeable, reused, or reassigned. - [Redirect to Google OAuth consent screen](https://docs.peridot.id/api-reference/auth/redirect-to-google-oauth-consent-screen.md): Redirects (HTTP 302) to Google's OAuth consent screen with `scope=profile email`. - [Google OAuth callback](https://docs.peridot.id/api-reference/auth/google-oauth-callback.md): Handles the redirect back from Google. On success it: - [Send an email login code](https://docs.peridot.id/api-reference/auth/send-an-email-login-code.md): Sends a 6-digit login code to the address via the Brevo sender (10-minute TTL, 60-second resend cooldown, 5 requests/hour per address). Always returns `{ ok: true }` — even for unknown addresses — so the response never reveals whether an address has an account. New addresses verify the same way, the… - [Verify an email login code](https://docs.peridot.id/api-reference/auth/verify-an-email-login-code.md): Consumes the 6-digit code (single-use, max 5 attempts, then locked). Returning credentials get `pid_access`/`pid_refresh` cookies directly; new addresses get `{ pendingClaim: true }` plus the `pid_claim` cookie for the PID picker. With `returnTo` (allowlisted, or registered to your app via `clientId… - [Pending PID claim, if any](https://docs.peridot.id/api-reference/auth/pending-pid-claim-if-any.md): Returns the pending post-auth claim from the `pid_claim` cookie (verified credential, no identity yet) for the claim picker UI. - [Claim a pending credential under a fresh PID handle](https://docs.peridot.id/api-reference/auth/claim-a-pending-credential-under-a-fresh-pid-handle.md): One transaction creates the identity (`@pid`, permanent) + profile + credential and consumes the single-use claim ticket. Issues the session cookies. When the claim came from a relying-party login, the response also carries the SSO `pidCode` plus the validated `redirectTo` — the hosted page… - [Exchange a one-time SSO code for the identity](https://docs.peridot.id/api-reference/auth/exchange-a-one-time-sso-code-for-the-identity.md): Consumes a `pid_code` issued by a Google or passkey login with `returnTo` (optionally bound to a `clientId`) and returns the PeridotID identity. Codes are single-use and expire after ~5 minutes. Call server-to-server; when the code is bound to an app, the same `clientId` must be presented. - [Client-credentials token for an app backend](https://docs.peridot.id/api-reference/auth/client-credentials-token-for-an-app-backend.md): Machine-to-machine token bound to the app owner's pid, carrying the app's clientId. Use `Authorization: Bearer `. `clientSecret` is required when the app has one set. - [Mint a pid_code for the current session](https://docs.peridot.id/api-reference/auth/mint-a-pid_code-for-the-current-session.md): Issues a one-time pid_code for the caller's existing session — no re-authentication. Powers consent screens on PeridotID-hosted pages: the user approves, the page redirects itself to returnTo with the code. Cookie-authenticated (same-site pages only). returnTo follows the same rules as login (loopba… - [Rotate the refresh token](https://docs.peridot.id/api-reference/auth/rotate-the-refresh-token.md): Rotates the refresh token and issues a fresh access token. The current `pid_refresh` cookie is **invalidated** and replaced with a new one (rotation), so each refresh token can be used exactly once. - [Log out](https://docs.peridot.id/api-reference/auth/log-out.md): Revokes the current refresh token and clears both session cookies. Idempotent — safe to call when already logged out. - [List my registered apps](https://docs.peridot.id/api-reference/apps/list-my-registered-apps.md) - [Register a third-party app](https://docs.peridot.id/api-reference/apps/register-a-third-party-app.md): Registers an app for "Sign in with PeridotID" and returns its public `client_id`. Pass just a name — allowed origins are managed after via PATCH (the exact return URL is passed in code at login time). Authenticated — the caller becomes the app owner. - [Generate or rotate an app's backend secret](https://docs.peridot.id/api-reference/apps/generate-or-rotate-an-apps-backend-secret.md): Returns a `pidsk_…` secret **once** — only its SHA-256 hash is stored, so an existing secret can never be read back, only replaced. Afterwards, code exchange for this app requires the secret. Backend-only: never ship it in frontend code. Owners only. - [Set the app's ledger-callback endpoint](https://docs.peridot.id/api-reference/apps/set-the-apps-ledger-callback-endpoint.md): PeridotID POSTs signed ledger events here (e.g. `fiat.transfer.posted` when funds land on this app's escrow account). Returns a `pidwh_…` HMAC signing secret **once**; every callback carries `X-Pid-Signature: sha256=` over the raw body and `X-Pid-Event`. https required (http allowed for localho… - [Remove the app's ledger-callback endpoint (and secret)](https://docs.peridot.id/api-reference/apps/remove-the-apps-ledger-callback-endpoint-and-secret.md) - [Per-app fee schedule (topup / transaction / withdraw)](https://docs.peridot.id/api-reference/apps/per-app-fee-schedule-topup-transaction-withdraw.md) - [Set one operation's per-app fee (stacks on the global fee)](https://docs.peridot.id/api-reference/apps/set-one-operations-per-app-fee-stacks-on-the-global-fee.md) - [Update one of my apps](https://docs.peridot.id/api-reference/apps/update-one-of-my-apps.md): Rename, change allowed origins, or disable the app. Owners only. - [Get the current identity](https://docs.peridot.id/api-reference/identity/get-the-current-identity.md): Returns the identity of the authenticated user. The identity ID (`@pid`, e.g. `ifal@pid`) is permanent — user-chosen once at onboarding, never changed, reused, or reassigned, regardless of which credentials the user links, unlinks, or how the profile is updated. - [List login credentials](https://docs.peridot.id/api-reference/identity/list-login-credentials.md): Lists every login credential (provider, email, linked/last-login time) linked to the current identity. Each row is one way to prove ownership of the PID. - [Unlink a login credential](https://docs.peridot.id/api-reference/identity/unlink-a-login-credential.md): Removes a credential from the current identity. Fails with `400` when it is the identity's last credential — every identity must keep at least one way to log in. - [Get the current profile](https://docs.peridot.id/api-reference/profile/get-the-current-profile.md): Returns the profile of the authenticated user. - [Update the current profile](https://docs.peridot.id/api-reference/profile/update-the-current-profile.md): Updates one or more mutable profile fields (display name, avatar, locale). Only provided fields are changed. The PID (`@pid`) is immutable and is never updated here — there is no endpoint that changes, reuses, or reassigns it. - [Get the current wallet](https://docs.peridot.id/api-reference/wallet/get-the-current-wallet.md): Returns the wallet associated with the authenticated PID. The wallet belongs to the PID, never to a specific login provider — unlinking Google does not affect it. - [Create the wallet](https://docs.peridot.id/api-reference/wallet/create-the-wallet.md): Creates the wallet for the authenticated PID with a user-supplied Solana address. The chain is fixed to `solana` for V3 (single-chain MVP). - [Rotate the on-chain authority to a registered replacement credential](https://docs.peridot.id/api-reference/wallet/rotate-the-on-chain-authority-to-a-registered-replacement-credential.md): Credential-lifecycle rotation: register the replacement credential first (approval flow), then the current key authorizes on-chain `update_authority` here. The old credential is revoked in DB only after the chain confirms the new authority. - [Quote the realtime network fee for a sponsored withdrawal](https://docs.peridot.id/api-reference/wallet/quote-the-realtime-network-fee-for-a-sponsored-withdrawal.md): Returns the backend's realtime network-cost estimate plus the fixed protocol percentage for the active fee policy. The client signs the intent + policy (never amounts) and echoes the quoted network fee as the drift reference. - [Sponsored SOL/SPL withdrawal (passkey-authorized, V3)](https://docs.peridot.id/api-reference/wallet/sponsored-solspl-withdrawal-passkey-authorized-v3.md): The relayer submits and floats the network fee; the smart account reimburses the attested network cost in full plus the on-chain-recomputed protocol fee, atomically. The backend rejects drift beyond 120% of the quoted network fee. - [Quote the realtime network fee for a generic execute](https://docs.peridot.id/api-reference/wallet/quote-the-realtime-network-fee-for-a-generic-execute.md): Returns the backend's realtime network-cost estimate for the exact call shape (target, metas, data length) plus the fixed protocol percentage for the active fee policy. The client signs call_hash + policy (never amounts) and echoes the quoted network fee as the drift reference. - [Sponsored generic execute (passkey-authorized, V3)](https://docs.peridot.id/api-reference/wallet/sponsored-generic-execute-passkey-authorized-v3.md): One passkey-signed intent drives an arbitrary Solana call with the PDA as signer (wallet parity with the EVM `execute`). The relayer submits and floats the network fee; the smart account reimburses the attested network cost in full plus the on-chain-recomputed protocol fee, atomically. The backend r… - [Create a withdrawal intent](https://docs.peridot.id/api-reference/wallet/create-a-withdrawal-intent.md): Validates and persists a desired withdrawal (WITHDRAW_SOL / WITHDRAW_TOKEN) for the authenticated PID's smart account. Policy: account + registered passkey required, amount > 0, valid destination, destination != the smart account. The intent is single-use and expires (PRD_v4 §22) — reusing or exceed… - [Get an intent (ownership-checked)](https://docs.peridot.id/api-reference/wallet/get-an-intent-ownership-checked.md) - [Record a submitted on-chain transaction](https://docs.peridot.id/api-reference/wallet/record-a-submitted-on-chain-transaction.md): Records the on-chain submission against a pending, unexpired intent and marks it executed (single-use replay protection). Submission itself happens through the SDK/adapter. - [Get a transaction (ownership-checked)](https://docs.peridot.id/api-reference/wallet/get-a-transaction-ownership-checked.md) - [Get the authenticated PID's wallet chain rows](https://docs.peridot.id/api-reference/account/get-the-authenticated-pids-wallet-chain-rows.md) - [Ensure the identity's personal wallet](https://docs.peridot.id/api-reference/account/ensure-the-identitys-personal-wallet.md): Idempotent wallet setup for the authenticated PID (1 identity = 1 personal wallet): ensures the Solana `smart_account` chain row whose address is deterministically derived from the PID (PDA) and resolvable before any on-chain initialization, plus EVM counterfactuals. Smart-account initialization hap… - [List the wallet's chain accounts](https://docs.peridot.id/api-reference/account/list-the-wallets-chain-accounts.md) - [Live Solana activation view for the wallet](https://docs.peridot.id/api-reference/account/live-solana-activation-view-for-the-wallet.md) - [Activate the wallet's Solana smart account (relayer-sponsored, passkey-signed claim)](https://docs.peridot.id/api-reference/account/activate-the-wallets-solana-smart-account-relayer-sponsored-passkey-signed-claim.md) - [Live EVM activation view for one chain](https://docs.peridot.id/api-reference/account/live-evm-activation-view-for-one-chain.md) - [Deploy the EVM counterfactual for one chain (relayer-submitted, passkey-authorized)](https://docs.peridot.id/api-reference/account/deploy-the-evm-counterfactual-for-one-chain-relayer-submitted-passkey-authorized.md) - [Recent deposit intents for the caller (money-in history)](https://docs.peridot.id/api-reference/fiat/recent-deposit-intents-for-the-caller-money-in-history.md) - [Create a Checkout deposit intent (DOKU-hosted page, all channels)](https://docs.peridot.id/api-reference/fiat/create-a-checkout-deposit-intent-doku-hosted-page-all-channels.md): netAmountIdr is the NET credited to the user (minimum Rp50.000, API-enforced — no DOKU minimum). Total payable = net + PeridotID fee (0.1% min Rp100 no cap; Rp0 for verified apps) + the app's stacked fee (when `clientId` is given) + the selected method's DOKU gateway fee. All quotes are snapshotted… - [Quote a top-up (moves no money) — the PeridotID checkout summary](https://docs.peridot.id/api-reference/fiat/quote-a-top-up-moves-no-money-—-the-peridotid-checkout-summary.md): Returns the transparent breakdown shown before DOKU: Amount, PeridotID Fee (Rp0 for verified apps), App Fee, per-method Payment Gateway Fee and Total Payment, plus `paymentMethods[]` (per-category fee + total) so the summary updates on selection. Only **enabled** methods are listed; the wallet block… - [List DOKU payment methods + gateway fees (admin)](https://docs.peridot.id/api-reference/fiat/list-doku-payment-methods-+-gateway-fees-admin.md): The category catalog (Virtual Account / e-Wallet / Card / QRIS), one row each with an `enabled` switch and fee fields, plus any stored extra keys (specific codes / "*"). A method with no row shows as disabled at 0. - [Upsert one payment method's gateway fee / enabled state (admin)](https://docs.peridot.id/api-reference/fiat/upsert-one-payment-methods-gateway-fee-enabled-state-admin.md): `methodKey` is a DOKU method code, a category (VIRTUAL_ACCOUNT/EWALLET/CARD/QRIS/OTHER), or "*" (default). Resolution: exact code → category → "*". A row with `enabled=false` is an explicit off switch — the method is hidden in the wallet and no fee is charged (it shadows the "*" fallback). 0 min/max… - [Active platform service-fee policy](https://docs.peridot.id/api-reference/fiat/active-platform-service-fee-policy.md) - [Reconcile a created/processing row via DOKU transactions-status](https://docs.peridot.id/api-reference/fiat/reconcile-a-createdprocessing-row-via-doku-transactions-status.md) - [Sub-Account notification inbox (persist-first, corroborated before apply)](https://docs.peridot.id/api-reference/fiat/sub-account-notification-inbox-persist-first-corroborated-before-apply.md): No published V2 webhook signing contract — events persist on X-EXTERNAL-ID and apply only after DOKU transactions-status corroboration. Public; always 200 after persist. - [Webhook inbox read-model (admin)](https://docs.peridot.id/api-reference/fiat/webhook-inbox-read-model-admin.md) - [Backfill makeup points for settled deposits missing issuance (admin)](https://docs.peridot.id/api-reference/fiat/backfill-makeup-points-for-settled-deposits-missing-issuance-admin.md) - [Publish a new fee-policy version (admin, deactivates previous)](https://docs.peridot.id/api-reference/fiat/publish-a-new-fee-policy-version-admin-deactivates-previous.md) - [Get the DOKU-wide PPN rate (admin)](https://docs.peridot.id/api-reference/fiat/get-the-doku-wide-ppn-rate-admin.md) - [Set the DOKU-wide PPN rate (admin)](https://docs.peridot.id/api-reference/fiat/set-the-doku-wide-ppn-rate-admin.md): Applied to the payment-gateway fee of every method; shown to users folded into "Fee Transfer". - [Internal-credit balance (temporary Rail B, replayed — source labelled)](https://docs.peridot.id/api-reference/fiat/internal-credit-balance-temporary-rail-b-replayed-—-source-labelled.md): Temporary Internal Credit Ledger balance, replayed from the immutable journal (source: internal-credit — NOT the DOKU POINT Saldo). See docs/INTERNAL_CREDIT.md. - [Internal-credit journal export + replay verdict](https://docs.peridot.id/api-reference/fiat/internal-credit-journal-export-+-replay-verdict.md) - [Internal-credit send inquiry (moves no money, phase-1 policy enforced)](https://docs.peridot.id/api-reference/fiat/internal-credit-send-inquiry-moves-no-money-phase-1-policy-enforced.md): GROSS-in quote pinned by feePolicyVersion. Phase-1 policy (escrow-only): one side of every send must be an allowlisted app PID. - [Confirm an internal-credit send (atomic, idempotent)](https://docs.peridot.id/api-reference/fiat/confirm-an-internal-credit-send-atomic-idempotent.md): Posts sender-out + recipient-in + treasury-fee legs in one DB transaction. Re-confirming a posted intent returns the view. - [Cancel a created internal-credit intent](https://docs.peridot.id/api-reference/fiat/cancel-a-created-internal-credit-intent.md) - [Platform-wide internal-credit replay projection (admin)](https://docs.peridot.id/api-reference/fiat/platform-wide-internal-credit-replay-projection-admin.md) - [Verify the tamper-evident ledger hash chain (admin)](https://docs.peridot.id/api-reference/fiat/verify-the-tamper-evident-ledger-hash-chain-admin.md) - [Current chain head hash (publish externally to anchor) (admin)](https://docs.peridot.id/api-reference/fiat/current-chain-head-hash-publish-externally-to-anchor-admin.md) - [Chain posted rows that predate the chain (admin)](https://docs.peridot.id/api-reference/fiat/chain-posted-rows-that-predate-the-chain-admin.md) - [Deliver due third-party callbacks (cron) (admin)](https://docs.peridot.id/api-reference/fiat/deliver-due-third-party-callbacks-cron-admin.md) - [Inspect the callback outbox (admin)](https://docs.peridot.id/api-reference/fiat/inspect-the-callback-outbox-admin.md) - [Engage/clear the internal-credit migration freeze (admin)](https://docs.peridot.id/api-reference/fiat/engageclear-the-internal-credit-migration-freeze-admin.md) - [Validate a permission grant and return its canonical id + owner challenge](https://docs.peridot.id/api-reference/permissions/validate-a-permission-grant-and-return-its-canonical-id-+-owner-challenge.md): Pure scope-hygiene check mirroring `grantPermission` (kind rules, denylist, 30d TTL, caps) plus the canonical `permissionId` and the owner challenge (what the passkey signs). Authenticated; submission-time pid→account binding is enforced by the relay path. - [Selectors a nonfinancial permission may never invoke](https://docs.peridot.id/api-reference/permissions/selectors-a-nonfinancial-permission-may-never-invoke.md): Mirror of the on-chain `_deniedSelector` list (second layer only — exact target+selector scope is the first). - [Validate a session grant and return its canonical address + owner challenge](https://docs.peridot.id/api-reference/session-keys/validate-a-session-grant-and-return-its-canonical-address-+-owner-challenge.md): Pure scope-hygiene check mirroring `register_session` (24h TTL, game binding, upgrade snapshot shape) plus the canonical session PDA and the owner challenge (what the passkey signs). Authenticated; submission-time pid→vault binding is enforced by the relay path. - [Session bounds mirror of the on-chain constants](https://docs.peridot.id/api-reference/session-keys/session-bounds-mirror-of-the-on-chain-constants.md): 24h max TTL, 30min inactivity timeout, 205-byte record. - [List signing authorities (passkeys)](https://docs.peridot.id/api-reference/credentials/list-signing-authorities-passkeys.md): Returns the authenticated PID's active passkey authorities for their account. Public fields only — credential ID, last-used; never key material. - [Begin passkey registration](https://docs.peridot.id/api-reference/credentials/begin-passkey-registration.md): Returns WebAuthn registration options. When the account already has a credential, `isAdditional` is true and an `approval` challenge is included — the client must assert with an existing credential to authorize adding the new one (existing-credential approval rule). - [Complete passkey registration](https://docs.peridot.id/api-reference/credentials/complete-passkey-registration.md): Verifies the WebAuthn attestation against the single-use, expiring challenge and stores the public key + credential ID (public material only). Rejects a second credential without a valid existing-credential approval, a replayed challenge, or a duplicated credential ID. - [Begin credential authentication](https://docs.peridot.id/api-reference/credentials/begin-credential-authentication.md) - [Complete credential authentication](https://docs.peridot.id/api-reference/credentials/complete-credential-authentication.md) - [Revoke a signing authority](https://docs.peridot.id/api-reference/credentials/revoke-a-signing-authority.md): Revokes a passkey. The account must keep at least one active authority — revoking the last one is rejected (last-credential guard). - [Get this specification](https://docs.peridot.id/api-reference/openapi/get-this-specification.md): Returns the raw OpenAPI 3.0 document as `application/yaml`. ## OpenAPI Specs - [openapi](/openapi.yaml) This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.