# Tutorials - [Getting Started](/en/docs/getting-started) - [Create a Realm and Set Up the Basics](/en/docs/realm-setup): Create your own realm on the hosted instance at auth.fornetcode.com, then do the three things every new realm needs: open registration, publish your own Terms and Privacy Policy, and brand the auth pages. - [Architecture](/en/docs/architecture): Rust backend (hexagonal architecture) + React frontend (TanStack stack), deployed as a single process. - [Configuration](/en/docs/configuration): Herald manages all runtime configuration through a single TOML file. The file is read once at startup; there is no hot-reload -- changes require a process restart. - [Deployment](/en/docs/deployment): Run Herald on a single host with docker compose — three containers for the backend, database, and cache. - Authentication - [Passkey Authentication](/en/docs/auth/passkey): Herald's Passkey support is built on WebAuthn / FIDO2. The same credential works as a passwordless first factor (conditional UI) and as a second factor after password login. Password and TOTP are always kept as fallbacks so a lost device or unsupported browser never locks a user out. - [Email Code (OTP) Login](/en/docs/auth/email-otp): Herald's email-code login lets a user sign in by entering a one-time code sent to their email, with no password. A Realm Admin turns it on under Settings, and can let brand-new emails auto-register on first login. The code is short-lived, attempt-limited, and rate-limited per email and per IP. Password, TOTP, and Passkey remain available as alternatives. - [Apple native login (in an iOS app)](/en/docs/auth/apple-native): An integrator's iOS app uses ASAuthorizationAppleIDProvider to obtain an Apple-issued identityToken and POSTs it to Herald for verification, without ever leaving the app. Herald only validates the credential — it does not call Apple's token endpoint and never touches client_secret. Two branches are supported: a direct session and a downstream Code+PKCE code. The path resolves to the same Herald account as the Apple web redirect login. - [LDAP Corporate Directory Login](/en/docs/auth/ldap): Herald's LDAP login lets employees sign in with the username and password they already have in the corporate directory (Active Directory, OpenLDAP, any LDAPv3 directory). A Realm Admin configures the directory connection under Settings, a corporate-account entry appears on the login page, and the first successful login provisions an account with no local password. The directory is only the credential authority: Herald never writes to or syncs the directory. - Billing - [Billing Architecture](/en/docs/billing/overview): Herald's billing system does not keep a local product catalog. Payment providers like Stripe and Creem own Product, Price, Checkout, and the subscription lifecycle; Herald keeps only the minimum data needed for authorization and credit grants — enough to run a paywall that stays in sync with every payment and refund. - [Stripe Integration](/en/docs/billing/stripe-payment): A hands-on Stripe integration guide for Realm Admins. Follow it end to end and your users will be able to complete subscription payments and credit-pack purchases through Stripe. - [Creem Integration](/en/docs/billing/creem-payment): A hands-on Creem integration guide for Realm Admins. Follow it end to end and your users will be able to complete subscription payments through Creem. - [WeChat Pay Integration](/en/docs/billing/wechat-payment): A hands-on WeChat Pay integration guide for Realm Admins. Configure merchant credentials, create order-priced mappings, and collect payments through PC QR codes (Native) or in-WeChat checkout (JSAPI). - [App Store / Google Play (IAP) Integration](/en/docs/billing/iap): A hands-on IAP integration guide for Realm Admins. Configure subscriptions, credit packs, buyouts, and non-renewing subscriptions through App Store and Google Play. - [Wallet Payments (Google Pay / Apple Pay)](/en/docs/billing/wallet-payments): Enable Google Pay and Apple Pay on your existing Stripe and Creem checkout rails. Web wallets appear on the hosted checkout pages; mobile apps declare the PaymentIntent flow and confirm with native wallet SDKs. - [Multi-Currency Pricing](/en/docs/billing/multi-currency): Sell one Stripe product at several currencies. Covers purchase-page currency grouping with explicit selection, and the api-ext currency resolution endpoints for third-party apps. - [Invoice Management](/en/docs/billing/invoice): Herald supports two kinds of invoices: invoices created manually by admins, and invoices requested by users through self-service. Both share the same underlying data model, but differ in entry point and permissions. - [Credit Notes](/en/docs/billing/credit-note): Herald's invoice system supports three sources: Stripe external sync, Creem MoR sync, and Herald self-issued (manual). A Credit Note records a refund voucher at the invoice level, so an invoice's refunded amount and remaining payable stay consistent with the actual money collected. - Points - [Points Accounts (Credit Accounts)](/en/docs/points/account): A Points Account is the unit of isolation for credits in a Realm: each account holds an independent balance, covers a set of Client Apps, and only covered apps can spend from it. Credits arrive typed (top-up, subscription, registration, free-periodic, granted) and the spendable balance is derived from a per-grant ledger — the balance you see is always the balance you can spend. - [Points Consumption](/en/docs/points/consumption): Your backend spends credits with one atomic API call: POST /api/ext/points/{realmId}/consume. Herald routes the spend to the Points Accounts covering that Client App, deducts quota windows first and pool balances nearest-expiry-first, and either books every transaction or rolls the whole call back — there is never a partial deduction. - [Points Quotas (Rolling Windows)](/en/docs/points/quota): Configure rolling-window quotas for subscription and free-periodic credits. A single entitlement can carry multiple windows like {5h: 500, week: 5000, month: 20000}; the available quota at any moment is the minimum remaining across all windows. - [Paywall configuration (payment-granted roles)](/en/docs/points/paywall): Configuring granted roles on an Entitlement Mapping is the paywall in Herald: a successful payment grants the roles, cancellations and full refunds revoke them (partial refunds keep buyout roles), and your application gates access through the existing RBAC runtime. - Integration - [Integrating a Third-Party Backend with Herald](/en/docs/integration/third-party-backend): Herald handles users, login, permissions, and billing, and issues Bearer access/refresh tokens. Your backend only writes business logic, calling Herald's authentication and billing interfaces through the SDK. - [Custom User UI](/en/docs/integration/custom-user-ui): An integrator can build their own end-user frontend — register, login, account center — on their own origin with no backend of their own. The frontend authenticates against Herald with browser-held Bearer tokens issued by the /login endpoint, and calls /api/user/* directly. This is the self-built UI path, distinct from White-label branding and from the backend-mediated OAuth (BFF) path. - [MCP Integration for AI Agents](/en/docs/integration/mcp): Connect Claude Code, VS Code, or any MCP client to your Herald realm with OAuth — no API keys, no client secrets. Sign in once in the browser and query your realm through nine read-only tools. - [OpenID Connect Integration](/en/docs/integration/oidc): Connect Grafana, Vault, or any standard OIDC client to your Herald realm with a single issuer URL — discovery, RS256 id_token, and userinfo ride on the existing OAuth 2.0 Authorization Code + PKCE flow, no Herald SDK required. - Customization - [White-label (Realm Branding)](/en/docs/customization/branding): Herald lets a Realm Admin brand the hosted auth pages — logo, accent color, background, footer, and login/register copy — so end users see the tenant's brand instead of the default Herald chrome. Branding is per-Realm, covers every auth-flow page and sub-state, and falls back to the Herald defaults on any missing or broken asset. - [Custom Domain](/en/docs/customization/custom-domain): A Realm Admin can register a branded login domain (like login.acme.com) for their Realm, get CNAME guidance, and watch the CNAME/TLS status go live. The domain is globally unique and saving applies immediately — no separate publish step. Herald only authorizes the reverse proxy to issue TLS certificates for domains a Realm Admin has registered. - Openapi - Api keys - [Get API Key roles](/en/docs/openapi/api-keys/adminGetApiKeyRoles): Returns the list of roles assigned to a specific API Key. - [Update API Key roles](/en/docs/openapi/api-keys/adminUpdateApiKeyRoles): Replaces all roles assigned to an API Key. Empty array clears all roles. Builtin roles cannot be assigned to API Keys (returns 400). - [Create a new API Key](/en/docs/openapi/api-keys/create_api_key): Generates a new API key for the specified realm. The plaintext key is returned only in this response and cannot be retrieved later. - [Delete an API Key](/en/docs/openapi/api-keys/delete_api_key): Permanently deletes an API key. - [Get a specific API Key by ID](/en/docs/openapi/api-keys/get_api_key): Retrieves details of an API key. Hash and plaintext are never exposed. - [List all API Keys for a realm](/en/docs/openapi/api-keys/list_api_keys): Returns a paginated list of API keys. Hash and plaintext are never exposed. - [Rotate an API Key](/en/docs/openapi/api-keys/rotate_api_key): Generates a new key for the API key, replacing the old hash. The old key is immediately invalidated. Returns the new plaintext key (shown once). - [Update an API Key](/en/docs/openapi/api-keys/update_api_key): Partially updates an API key's name, enabled status, or expiration. - Audit - [Get a single audit event by ID](/en/docs/openapi/audit/get_audit_event) - [List audit events with optional filters and pagination](/en/docs/openapi/audit/list_audit_events) - Auth - [Confirm email change with verification code](/en/docs/openapi/auth/change_email_confirm): Completes the email change using the code that arrived via the frontend confirm-page link in the confirmation email; the frontend calls this endpoint with the user's current session, which must be the code owner. - [Request to change the email address for the authenticated user](/en/docs/openapi/auth/change_email_request): Sends a confirmation link to the new email address. The link points to the first-party frontend confirm page, which the user must open in their logged-in browser so the frontend can call the confirm endpoint with the current session. Returns 400 when the realm has no usable email channel. - [Get registration configuration status for a realm](/en/docs/openapi/auth/get_registration_status): Returns whether user registration is enabled for the specified realm. - [Public visibility of the self-service entry. Fail-closed: a missing toggle row resolves to `enabled: false`.](/en/docs/openapi/auth/get_signup_status) - [Get Turnstile configuration status for a Client App](/en/docs/openapi/auth/get_turnstile_status): Returns whether Turnstile verification is enabled for the specified Client App and the site key to render the widget. Turnstile is fully delegated to the Client App (D-PROTECT-01); the legacy realm_config source is no longer read. A missing or disabled Client App is rejected with 401, matching the other anonymous Client-App-bound endpoints. Pass `clientId` as a query parameter. - [Handle_passkey_2fa_options](/en/docs/openapi/auth/handle_passkey_2fa_options) - [Handle_passkey_2fa_verify](/en/docs/openapi/auth/handle_passkey_2fa_verify) - [Handle_passkey_options](/en/docs/openapi/auth/handle_passkey_options) - [Handle_passkey_verify](/en/docs/openapi/auth/handle_passkey_verify) - [Verify TOTP code for two-factor authentication](/en/docs/openapi/auth/handle_verify_totp): Completes the login process for users with TOTP enabled. Accepts either a TOTP code from an authenticator app or a backup code. Creates a permanent session on success. - [Enterprise (LDAP) directory login.](/en/docs/openapi/auth/ldap_login): Authenticates the submitted username/password against the Realm's configured directory (search-then-bind), matches or JIT-provisions the Herald account, and then runs the full existing login pipeline (2FA / consent / OAuth code / session issuance), auditing `method="ldap"`. - [Public LDAP enablement flag for a Realm (login-page entry visibility). Fail-closed: absent/malformed/disabled/insecure-channel config → false.](/en/docs/openapi/auth/ldap_status) - [Authenticate user with email/username and password](/en/docs/openapi/auth/login): Supports both email and username login. Returns a session token or requires TOTP verification if two-factor authentication is enabled for the user. - [Logout](/en/docs/openapi/auth/logout) - [Refresh](/en/docs/openapi/auth/refresh) - [Register new user account](/en/docs/openapi/auth/register): Creates a new user account with email and optional username. If email verification is required for realm, account will be created in a pending state and a verification email will be sent. - [Confirm password reset with verification code](/en/docs/openapi/auth/reset_password_confirm): Completes password reset process using verification code sent to user's email. - [Request password reset](/en/docs/openapi/auth/reset_password_request): Initiates a password reset process by sending a verification code to user's email address. Always returns success to prevent email enumeration. - [Email the 6-digit verification code the signup flow will later consume.](/en/docs/openapi/auth/send_email_code): Only the admin realm hosts this entry; any other `realmId` is rejected. No Turnstile here (same posture as the other code-only senders — the per-IP + per-mailbox rate limits carry the anti-bombing defense), and the platform toggle gates it fail-closed so the code sender can never outlive the signup entry it serves. - [Send an email OTP login code.](/en/docs/openapi/auth/send): Issues a one-time verification code for an existing active user, or — if the Realm has OTP auto-register enabled — for an unregistered email after consent is expressed. Partially enumeration-resistant: a 200 is returned for a known but non-active user (no code sent). An explicit 409 is still returned for an unregistered email when auto-register is off — the same registration-status bit the register endpoint exposes by design (both paths sit behind Turnstile and per-email/IP rate limits). - [Provision a new realm from an unauthenticated visitor and issue an immediate first-party admin-console session for the new realm admin.](/en/docs/openapi/auth/signup): Only the admin realm hosts this entry; any other `realmId` is rejected. Pre-flight defenses run before any realm is created: the platform toggle, human verification bound to the admin realm's `admin-web-console` Client App, and a same-IP 24h quota. - [Public passkey enablement flag for a Realm.](/en/docs/openapi/auth/status): Reads the `passkey` / `settings` config row and returns `{ enabled }`. The login page uses this to decide whether to render the passkey entry before any passkey endpoint is called, rather than discovering disablement via a 404 from `POST /login/passkey/options`. Anonymous (no authentication). - [Switch_client](/en/docs/openapi/auth/switch_client) - [Confirm email verification with code](/en/docs/openapi/auth/verify_email_confirm): Completes email verification process using verification code sent to user's email. Activates user account upon successful verification. - [Trigger email verification](/en/docs/openapi/auth/verify_email_trigger): Sends a verification email to specified email address with a confirmation link. Used to initiate email verification for pending accounts. - [Verify an email OTP login code and issue a session.](/en/docs/openapi/auth/verify): On a matching code: consumes it (one-time), then either logs the existing user in (login-as-consent gate) or auto-registers a new account (create-without-password → activate → register-as-consent) and issues a Bearer token family via `RedisBrowserTokenService`. Returns `BrowserTokenResponse`. - Billing - [Batch-save all price mappings for a product.](/en/docs/openapi/billing/batch_update_entitlement_mappings): Validation/permission order: 1. `billing.manage` (realm boundary + business permission). 2. If any update row carries a credit-strategy field → `points.manage`. 3. Credit-strategy field validation. Then the repository performs a single-transaction upsert of all the product's price rows: any row transitioning enabled true→false while protected by an active subscription rolls back the WHOLE transaction (409 with `{ activeSubscriptions }`). Cross-realm/product `mapping_id` tampering surfaces as 400. - [Cancel_payment_attempt](/en/docs/openapi/billing/cancel_payment_attempt) - [Cancel the caller's own subscription for a client app.](/en/docs/openapi/billing/cancel_subscription_for_client_app): Unlike the previous admin-only flip, this handler is a **user self-service** endpoint mounted on the browser-token router. It calls the **payment provider cancel API** (Stripe/Creem) and does **NOT** touch the local subscription row — local status is updated exclusively by the subsequent provider webhook (`customer.subscription.deleted` / `subscription.canceled`). If the provider call fails the error is surfaced to the caller and the local state is left untouched. Apple / Google in-app purchases cannot be canceled via a developer API (only via the App Store / Play Store by the user); this endpoint rejects them with 400. - [Create a Credit Bucket.](/en/docs/openapi/billing/create_credit_bucket_handler) - [Create_entitlement_mapping](/en/docs/openapi/billing/create_entitlement_mapping): Generic over provider (IAP, Stripe, Creem). Required permission: `billing.manage`; distribution-rule fields additionally require `points.manage` (mirrors the batch update permission model). A duplicate `(realm, provider, product, price)` row violates `uq_pem_realm_provider_product_price` and surfaces as HTTP 409. - [Create_payment_attempt](/en/docs/openapi/billing/create_payment_attempt) - [Delete a Credit Bucket (DELETE).](/en/docs/openapi/billing/delete_credit_bucket_handler): 204 on success; 409 `bucket_in_use` with `{ code, activeSubscriptions, holdersWithBalance, ruleReferences, historyReferences }` when in-flight subscriptions, residual balances, rule references or history exist. - [Fulfill_payment](/en/docs/openapi/billing/fulfill_payment) - [Get the bucket overview matrix.](/en/docs/openapi/billing/get_bucket_overview_handler): Returns `{ rows: OverviewRow[], grandTotal: ByCreditType }` — `grandTotal` is a SEPARATE top-level field, not appended to rows. - [Get a single Credit Bucket with coverage set and attached mappings.](/en/docs/openapi/billing/get_credit_bucket_handler) - [Get a single entitlement mapping](/en/docs/openapi/billing/get_entitlement_mapping) - [Get current user's subscription history for a specific subscription](/en/docs/openapi/billing/get_my_subscription_history) - [Get_payment_attempt_status](/en/docs/openapi/billing/get_payment_attempt_status) - [Payment statistics for the admin statistics page.](/en/docs/openapi/billing/get_payment_stats): Counts only finalized attempts (Succeeded/Failed) attributed to their initiation day (`created_at`). Requires the `billing.view` permission. - [Get_purchase_history](/en/docs/openapi/billing/get_purchase_history) - [Get_realm_purchase_history](/en/docs/openapi/billing/get_realm_purchase_history) - [Get subscription for a client app](/en/docs/openapi/billing/get_subscription_for_client_app) - [Get subscription history for a specific subscription](/en/docs/openapi/billing/get_subscription_history) - [Get a specific subscription](/en/docs/openapi/billing/get_subscription) - [Handle_apple_webhook](/en/docs/openapi/billing/handle_apple_webhook) - [List all Credit Buckets for a realm.](/en/docs/openapi/billing/list_credit_buckets_handler) - [List entitlement mappings for a realm](/en/docs/openapi/billing/list_entitlement_mappings) - [List current user's subscription history with filtering and pagination](/en/docs/openapi/billing/list_my_subscription_history) - [List enabled one-time entitlement mappings for a realm](/en/docs/openapi/billing/list_one_time_mappings) - [List purchasable price-level options for a client app.](/en/docs/openapi/billing/list_purchase_options): Returns a FLAT list of enabled price-granularity mappings (recurring + one_time) for the purchase page; the frontend groups by `external_product_id` / billing period. Replaces the purchase page's dependency on `list_one_time_mappings` (which only covered one_time). - [List subscription history with filtering and pagination](/en/docs/openapi/billing/list_subscription_history) - [List subscriptions for a realm](/en/docs/openapi/billing/list_subscriptions) - [Submit_iap_receipt](/en/docs/openapi/billing/submit_iap_receipt) - [Sync provider products into entitlement mappings](/en/docs/openapi/billing/sync_provider_products) - [Update a Credit Bucket (PUT). Coverage set is fully replaced.](/en/docs/openapi/billing/update_credit_bucket_handler) - [Update an entitlement mapping](/en/docs/openapi/billing/update_entitlement_mapping) - Billing invoice - [Apply_invoice](/en/docs/openapi/billing-invoice/apply_invoice) - [Create_credit_note](/en/docs/openapi/billing-invoice/create_credit_note) - [Create_invoice](/en/docs/openapi/billing-invoice/create_invoice) - [Download_invoice_pdf](/en/docs/openapi/billing-invoice/download_invoice_pdf) - [Download_my_invoice_pdf](/en/docs/openapi/billing-invoice/download_my_invoice_pdf) - [Per-resource invoice apply-eligibility (read-only, context-level).](/en/docs/openapi/billing-invoice/get_invoice_apply_eligibility): Decision order: ownership → provider → policy → seller config → external-invoice-exists, then delegates the verdict to the pure `determine_invoice_apply_route`. The endpoint resolves facts; the pure function owns the rules (single home — avoids divergence from the write path `validate_invoice_creation_policy` / `apply_invoice`). - [Get_invoice](/en/docs/openapi/billing-invoice/get_invoice) - [Get_my_invoice_scoped](/en/docs/openapi/billing-invoice/get_my_invoice_scoped) - [Get_seller_config](/en/docs/openapi/billing-invoice/get_seller_config) - [Issue_invoice](/en/docs/openapi/billing-invoice/issue_invoice) - [Admin read-only attribution anomaly discovery.](/en/docs/openapi/billing-invoice/list_attribution_anomalies): Returns two anomaly classes: - `unattributed_invoices`: externally-synced invoices that lack both `subscription_id` and `payment_attempt_id` (reuses `list_admin` with `attribution=Missing`, capped at the first 100 rows). - `payments_without_invoice`: succeeded renewal / one-time payment attempts inside a 90-day window with no invoice linked via `invoice.payment_attempt_id` (anti-join read; no writes). Permission reuses `require_billing_permission(..., "view")` — same model as `GET /invoices`. Realm isolation is enforced both by the repository (`realm_id` filter) and the shared middleware. - [List_invoices](/en/docs/openapi/billing-invoice/list_invoices) - [List_my_invoices](/en/docs/openapi/billing-invoice/list_my_invoices) - [Mark_paid](/en/docs/openapi/billing-invoice/mark_paid) - [Update_invoice](/en/docs/openapi/billing-invoice/update_invoice) - [Upsert_seller_config](/en/docs/openapi/billing-invoice/upsert_seller_config) - [Void_invoice](/en/docs/openapi/billing-invoice/void_invoice) - Billing.payment providers - [List_payment_providers](/en/docs/openapi/billing.payment-providers/list_payment_providers) - Client - [Create a new client app](/en/docs/openapi/client/create_client_app): Creates a new OAuth client application with the specified configuration. - [Delete a client app](/en/docs/openapi/client/delete_client_app): Deletes an OAuth client application and its associated roles. Built-in client apps (admin console, user account center, the realm's built-in API Key client app, and the built-in MCP client) cannot be deleted — disabling is the supported off-switch for the MCP client. - [Get a specific client app by ID](/en/docs/openapi/client/get_client_app): Retrieves the details of a specific OAuth client application. - [List client applications](/en/docs/openapi/client/list_client_apps): List all OAuth client applications configured for the specified realm with pagination. Requires `clients.view` permission. - [Update a client app configuration](/en/docs/openapi/client/update_client_app): Updates the configuration of an existing OAuth client application. Optionally regenerate the client secret. - Dashboard - [Get dashboard statistics for a realm](/en/docs/openapi/dashboard/get_dashboard_stats) - Device - [Device_authorize](/en/docs/openapi/device/device_authorize) - [Device_confirm](/en/docs/openapi/device/device_confirm) - [Device_token](/en/docs/openapi/device/device_token) - [Device_verify](/en/docs/openapi/device/device_verify) - Ext - [Check user permissions](/en/docs/openapi/ext/check_permission): This endpoint allows third-party applications to check if a user has specific permissions using their browser access token. # Authentication Requires valid API Key via X-API-Key header # Example ```json { "accessToken": "user-access-token", "rules": [ {"resource": "article", "action": "read"}, {"resource": "article", "action": "write"} ] } ``` - [Consume points from user account](/en/docs/openapi/ext/consume_points_ext): Consumes points from a user's account for paid operations. Returns transaction details including the new balance. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X POST \ https://api.example.com/api/ext/points/realm123/consume \ -H "X-API-Key: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "userId": "user-123", "clientAppId": "app-abc", "amount": 100, "description": "AI API call" }' ``` - [Create a new client app in a realm](/en/docs/openapi/ext/create_client_app): Creates a client app in the specified realm. Only principals with `clients:manage` permission in the target realm may invoke this endpoint. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `clients:manage` permission in the target realm. - [Create a new realm](/en/docs/openapi/ext/create_realm): Creates a new realm in the system. Only principals with `realm:manage` permission in the admin realm may invoke this endpoint. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `realm:manage` permission in the `admin` realm. - [Create a new user in a realm](/en/docs/openapi/ext/create_user): Creates a user in the specified realm. Only principals with `users:create` permission in the target realm may invoke this endpoint. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `users:create` permission in the target realm. - [Get_balance_ext](/en/docs/openapi/ext/get_balance_ext) - [Get a single client app by ID within a realm](/en/docs/openapi/ext/get_client_app): Returns detailed information for the specified client app. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `clients:view` permission in the target realm. - [Get the currency set supported by a purchasable entitlement](/en/docs/openapi/ext/get_entitlement_currencies): Returns the deduplicated currencies covered by the entitlement's enabled Stripe mapping rows (subscription-type and one-time-type rows both count). Creem/IAP rows are excluded: their pricing is provider/store-side and carries no per-currency price rows. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. - [Get purchasable one-time entitlement mappings](/en/docs/openapi/ext/get_one_time_mappings): Returns enabled one-time entitlement mappings that have provider product info configured. Used by frontend/SDK to display purchasable products. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. - [Get a single realm by ID](/en/docs/openapi/ext/get_realm): Returns detailed information for the specified realm. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `realm:view` permission in the target realm. Every principal may only view its own realm (no super-admin). - [Get subscription status for a client app](/en/docs/openapi/ext/get_subscription): Returns subscription information for the specified client app. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the client app. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X GET \ https://api.example.com/api/ext/subscription/client-app-123 \ -H "X-API-Key: your-api-key" ``` - [Get transaction by external reference ID](/en/docs/openapi/ext/get_transaction_by_external_ref_ext): Returns details of a specific points transaction by `externalRefId`. - [Get transaction by ID](/en/docs/openapi/ext/get_transaction_ext): Returns details of a specific points transaction. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X GET \ https://api.example.com/api/ext/points/realm123/transactions/uuid \ -H "X-API-Key: your-api-key" ``` - [Get a single user by ID within a realm](/en/docs/openapi/ext/get_user): Returns detailed information for the specified user. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `users:view` permission in the target realm. - [Grant points to a user account (SDK)](/en/docs/openapi/ext/grant_points_ext): Grants points to a user's account from a third-party application. Returns transaction details including the new balance. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X POST \ https://api.example.com/api/ext/points/realm123/grant \ -H "X-API-Key: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "userId": "user-123", "amount": 100, "reason": "Promotional grant" }' ``` - [List client apps in a realm](/en/docs/openapi/ext/list_client_apps): Returns all client apps in the specified realm. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `clients:view` permission in the target realm. - [List realms](/en/docs/openapi/ext/list_realms): Returns the realms visible to the caller. Principals in the `admin` realm see all realms (platform-level enumeration); every other principal sees only its own realm. Realm-level detail endpoints remain strictly own-realm — this list is the only platform-wide view. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `realm:view` permission in their own realm. - [List users in a realm](/en/docs/openapi/ext/list_users): Returns a paginated list of users in the specified realm. Default page_size is 20, maximum is 100. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `users:view` permission in the target realm. - [Resolve an entitlement's default price row by currency](/en/docs/openapi/ext/resolve_default_price): Filters the entitlement's enabled Stripe mapping rows by the requested currency (plus optional billing type/period filters) and returns the unique match. Fail-loud by design: 404 when no row matches the currency (no secondary-currency fallback), 409 when multiple rows remain and the caller must narrow by billing type/period. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. - Legal - [Discard the staged draft.](/en/docs/openapi/legal/admin_discard_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Idempotent: discarding a missing draft returns 204. The published version table is untouched. - [Get the staged draft for an agreement type.](/en/docs/openapi/legal/admin_get_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Returns 404 when no draft exists for the type (the admin form treats this as "start a new draft"). - [Get a single published version's full body by id (admin history view).](/en/docs/openapi/legal/admin_get_version): Admin — requires first-party Bearer + `settings.view` (realm pinned by the session token) (same gate as `admin_list_agreements`, since this is a read of the same history the list already exposes). Returns 404 when the id does not resolve. The `realmId` path segment scopes the permission check; the version itself is looked up by primary key (history rows already surfaced by the list endpoint are guaranteed to belong to the realm or be the platform default). - [List both agreement types with their source, current effective version, and version history for the realm.](/en/docs/openapi/legal/admin_list_agreements): Admin — requires first-party Bearer + `settings.view` + `has_access_to_realm`. `source` is derived per-type from `has_custom` (Custom when a realm custom version exists, otherwise Default). - [Publish a new per-realm custom agreement version.](/en/docs/openapi/legal/admin_publish_custom): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Unknown `agreementType` → 400; non-object/empty `content` → 400 (also enforced in the service). On success the service records an `agreement.published` audit event and returns the new version. - [Publish the staged draft as a new effective version.](/en/docs/openapi/legal/admin_publish_from_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Reads the draft, creates a new immutable `legal_agreement_version` row (advancing `version_no`, recording an `agreement.published` audit event), and clears the draft. Returns 404 when no draft exists for the type. This is the only path the admin UI uses to publish — there is no "publish without a draft" entry in the UI (the legacy `PUT /agreements/{type}` handler remains for backward compatibility). - [Revert a realm's agreement to the platform default.](/en/docs/openapi/legal/admin_revert_to_default): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Implemented as **default-follow semantics** in the service: a realm-scoped marker version is appended with `source = default` (new `version_id`, monotonic `version_no`), after which effective resolution follows the latest platform default rather than a frozen copy. History remains append-only; no prior rows are deleted. The handler is a thin pass-through. - [Save (upsert) a draft for an agreement type.](/en/docs/openapi/legal/admin_save_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). A repeat save overwrites the prior draft. Does NOT publish — the agreement stays unchanged for end users until POST `/publish`. - [Get the full localized detail of a single agreement type for a realm.](/en/docs/openapi/legal/get_agreement): Public — no Bearer identity. Unknown `agreementType` → 400; no effective version deployed → 404. - [Reconsent gate verdict for the calling user across both agreement types.](/en/docs/openapi/legal/get_consent_status): Self-service — requires Bearer identity. The realm is the one pinned by the bearer token (each user may only inspect their own realm's consent state). - [List the current effective agreements for a realm (Terms + Privacy).](/en/docs/openapi/legal/list_agreements): Public — no Bearer identity. Each agreement type resolves to its current effective version (realm custom if present, otherwise platform default); missing types are skipped. Returns 404 when no type resolves, which signals a seed-missing deployment anomaly (the platform default templates should always be present). - [Record the user's explicit consent to one or more agreement versions.](/en/docs/openapi/legal/record_consent): Self-service — requires Bearer identity; the realm is the one pinned by the bearer token. Each `version_id` must equal the current effective version for its type (enforced in the service layer, BE-D04); a stale version surfaces as 409 so the client re-reads the effective version. Returns 204 on success. The upsert is idempotent on a repeat of the same version. - Oauth - [Apple native login.](/en/docs/openapi/oauth/apple_native_login): Verifies an Apple `identityToken` (from `ASAuthorizationAppleIDProvider`) server-side and, depending on `downstreamState`, issues either a direct browser session (Bearer token family bound to `clientId`) or a downstream authorization code for the Code+PKCE flow. Requires the realm to have an enabled Apple provider. - [Create OAuth provider configuration](/en/docs/openapi/oauth/create_oauth_config) - [Delete OAuth provider configuration](/en/docs/openapi/oauth/delete_oauth_config) - [Get OAuth provider configuration by provider type](/en/docs/openapi/oauth/get_oauth_config) - [Google One Tap login.](/en/docs/openapi/oauth/google_one_tap): Verifies a Google One Tap (GIS) ID Token server-side and, depending on `downstreamState`, issues either a direct browser session (Bearer token family bound to `clientId`) or a downstream authorization code for the Code+PKCE flow. Requires the realm to have an enabled Google provider. - [List all OAuth provider configurations for a realm](/en/docs/openapi/oauth/list_oauth_configs) - [MCP dynamic client registration — gated pass-through form (DEC-mcp-server-007). Advertised from the AS metadata as `registration_endpoint` because mainstream MCP clients refuse to connect without one; after the gate (rate limit + RFC 7591 shape + loopback redirect validation) it returns the realm's preset public `herald-mcp` client and persists NOTHING — the abuse surface therefore equals the preset-client status quo (the real security boundaries stay in authorize's loopback whitelist, mandatory PKCE, and the tool-layer RBAC/scope gates).](/en/docs/openapi/oauth/mcp_client_registration) - [RFC 9728 Protected Resource Metadata for a realm's MCP endpoint.](/en/docs/openapi/oauth/mcp_protected_resource_metadata) - [RFC 8414 Authorization Server metadata for the per-realm OAuth issuer behind the MCP resource. Served at the standard path-style well-known insertion (well-known between host and issuer path); the legacy OIDC append-form discovery URL keeps working unchanged.](/en/docs/openapi/oauth/oauth_authorization_server_metadata) - [OAuth authorize endpoint (Authorization Code + PKCE)](/en/docs/openapi/oauth/oauth_authorize): Initiates the Authorization Code + PKCE flow: 1. Validates client_id exists and is enabled 2. Validates redirect_uri is in whitelist (exact match only) 3. Validates PKCE code_challenge_method (must be S256 if provided) 4. Stores state token with PKCE parameters in Redis (5 minutes TTL) 5. Redirects to login page with OAuth parameters # Arguments * `realm_id` - Realm identifier * `params` - OAuth query parameters (client_id, redirect_uri, state, response_type, code_challenge, code_challenge_method) # Returns * 302 redirect to login page with OAuth parameters # Errors * 400 - Invalid parameters (missing client_id, redirect_uri, state, or code_challenge) * 400 - Invalid response_type (must be "code") * 400 - Unsupported code_challenge_method (must be "S256") * 404 - Client not found * 403 - Client app is disabled * 400 - Redirect URI not in whitelist - [Handle OAuth callback submitted as an HTML form (Apple `response_mode=form_post` sends the authorization response as a POST body instead of a redirect query). Same semantics as the GET variant.](/en/docs/openapi/oauth/oauth_callback_form) - [Handle OAuth callback from provider for a realm](/en/docs/openapi/oauth/oauth_callback) - [Initiate OAuth login flow for a realm](/en/docs/openapi/oauth/oauth_login) - [Oauth_token](/en/docs/openapi/oauth/oauth_token) - [Oidc_discovery](/en/docs/openapi/oauth/oidc_discovery) - [Oidc_jwks](/en/docs/openapi/oauth/oidc_jwks) - [Oidc_rotate_signing_key](/en/docs/openapi/oauth/oidc_rotate_signing_key) - [Oidc_userinfo](/en/docs/openapi/oauth/oidc_userinfo) - [Update OAuth provider configuration](/en/docs/openapi/oauth/update_oauth_config) - [Handle WeChat OAuth callback](/en/docs/openapi/oauth/wechat_callback) - [Generate WeChat authorization URL (QRconnect)](/en/docs/openapi/oauth/wechat_login) - [WeChat Mini Program login (code2session)](/en/docs/openapi/oauth/wechat_miniprogram_login) - Permission - [Add policy to role](/en/docs/openapi/permission/add_policy_to_role): Adds a permission policy to a role - [Assign roles to user](/en/docs/openapi/permission/assign_roles_to_user): Assigns one or more roles to a user - [Permission check](/en/docs/openapi/permission/check_permission): Spec: POST /api/permission/check - [Get role's policies](/en/docs/openapi/permission/get_role_policies): Returns all permission policies for a specific role - [Get user's roles](/en/docs/openapi/permission/permissionGetUserRoles): Returns all roles assigned to a specific user - [Remove policy from role](/en/docs/openapi/permission/remove_policy_from_role): Removes a specific permission policy from a role - [Remove role from user](/en/docs/openapi/permission/remove_role_from_user): Removes a specific role from a user - Permission definitions - [Create a new permission](/en/docs/openapi/permission-definitions/create_permission_definition): Create a new permission definition. Permission name must be in format 'resource.action' (e.g., 'users.manage'). Requires `permissions.manage` permission. - [Delete a permission](/en/docs/openapi/permission-definitions/delete_permission): Delete a permission definition. Built-in permissions cannot be deleted. Requires `permissions.manage` permission. - [Get permission by ID](/en/docs/openapi/permission-definitions/get_permission) - [List permissions in the realm](/en/docs/openapi/permission-definitions/list_permissions): List all permission definitions in the realm. Requires `permissions.view` permission. - [Update a permission](/en/docs/openapi/permission-definitions/update_permission): Update permission definition. Built-in permissions cannot be modified. Requires `permissions.manage` permission. - Points - [Points consumption statistics for the admin statistics page.](/en/docs/openapi/points/get_points_consumption_stats): Requires the `points.view` permission. Consumption is attributed to the transaction's creation day. - [Get the Realm's registration distribution rules.](/en/docs/openapi/points/get_registration_rules) - [Get points wallet for a specific user](/en/docs/openapi/points/get_wallet) - [Grant points to a user (admin)](/en/docs/openapi/points/grant_points) - [List points transactions with filters](/en/docs/openapi/points/list_transactions) - [List points wallets. Regular users see their own wallets; managers see all wallets in the realm.](/en/docs/openapi/points/list_wallets) - [Update_wallet_status](/en/docs/openapi/points/update_wallet_status) - [Atomically upsert the Realm's registration distribution rule set.](/en/docs/openapi/points/upsert_registration_rules) - Realm_config - [Batch create or update configs](/en/docs/openapi/realm_config/batch_upsert_realm_configs) - [Delete a config](/en/docs/openapi/realm_config/delete_realm_config) - [Get email configuration status for a realm](/en/docs/openapi/realm_config/email_status) - [Send a test email for a realm](/en/docs/openapi/realm_config/email_test) - [Get a single config](/en/docs/openapi/realm_config/get_realm_config) - [Get all configs of a given type](/en/docs/openapi/realm_config/list_realm_configs_by_type) - [Get all configs for a given realm](/en/docs/openapi/realm_config/list_realm_configs) - [Create or update a config (upsert)](/en/docs/openapi/realm_config/upsert_realm_config) - Realms - [Create a new realm](/en/docs/openapi/realms/create_realm) - [Get_feature_availability](/en/docs/openapi/realms/get_feature_availability) - [Get realm by ID](/en/docs/openapi/realms/get_realm) - [Caddy On-Demand TLS ask authorization endpoint.](/en/docs/openapi/realms/handle_custom_domain_authorize): Returns `200 {"authorized": true}` when `host` matches a published+enabled `custom_domain_mapping` row; `404` on a miss (Caddy declines TLS issuance); `401` when the `X-Herald-Ask-Key` header is missing or mismatches the configured shared secret. Never exposes realm information — this is a certificate-abuse gate. - [Discard white-label draft.](/en/docs/openapi/realms/handle_discard_white_label_draft) - [Get custom-domain management state.](/en/docs/openapi/realms/handle_get_custom_domain_config) - [Get realm Email OTP configuration](/en/docs/openapi/realms/handle_get_realm_email_otp_config): Retrieves the Email OTP configuration (login enabled + auto-register) for a realm. Requires 'settings.view' permission. Defaults to `{ enabled: false, auto_register: false }` when no config exists. - [Get realm Passkey configuration](/en/docs/openapi/realms/handle_get_realm_passkey_config): Retrieves the Passkey configuration for a realm. Requires 'settings.view' permission. - [Get realm TOTP configuration and statistics](/en/docs/openapi/realms/handle_get_realm_totp_config): Retrieves the TOTP configuration for a realm along with statistics about TOTP adoption including total users, enabled users, disabled users, and enablement rate. Requires 'settings.view' permission. - [Get white-label management state.](/en/docs/openapi/realms/handle_get_white_label_config) - [Publish request body or the existing white-label draft.](/en/docs/openapi/realms/handle_publish_white_label_config) - [Restore previous white-label settings.](/en/docs/openapi/realms/handle_restore_white_label_config) - [Save white-label draft without publishing.](/en/docs/openapi/realms/handle_save_white_label_draft) - [Update custom-domain configuration (writes the host→realm mapping).](/en/docs/openapi/realms/handle_update_custom_domain_config) - [Update realm Email OTP configuration](/en/docs/openapi/realms/handle_update_realm_email_otp_config): Enables/disables Email OTP login for a realm and separately controls whether unregistered emails are auto-registered on successful verification. Requires 'settings.manage' permission. Stored as `ConfigType::EmailOtp` / `config_key="settings"` JSON. - [Update realm Passkey configuration](/en/docs/openapi/realms/handle_update_realm_passkey_config): Updates the Passkey configuration for a realm, including whether Passkey authentication is enabled. Requires 'settings.manage' permission. - [Update realm TOTP configuration](/en/docs/openapi/realms/handle_update_realm_totp_config): Updates the TOTP (Time-based One-Time Password) configuration for a realm, including whether TOTP is enabled and whether it is force-required for all users. Requires 'settings.manage' permission. - [List realms with pagination](/en/docs/openapi/realms/list_realms_paginated) - [List all realms (non-paginated, deprecated - use list_realms_paginated)](/en/docs/openapi/realms/list_realms) - [Update realm](/en/docs/openapi/realms/update_realm) - Role definitions - [Assign permission to role](/en/docs/openapi/role-definitions/assign_permission_to_role) - [Create a new role](/en/docs/openapi/role-definitions/create_role): Create a new role definition. Requires `roles.manage` permission. - [Delete a role](/en/docs/openapi/role-definitions/delete_role): Delete a role definition. Built-in roles cannot be deleted. Requires `roles.manage` permission. - [Get permissions for a role](/en/docs/openapi/role-definitions/get_role_permissions) - [Get role by ID](/en/docs/openapi/role-definitions/get_role) - [List roles in the realm](/en/docs/openapi/role-definitions/list_roles): List all role definitions for the admin-web-console client. Requires `roles.view` permission. - [Remove permission from role](/en/docs/openapi/role-definitions/remove_permission_from_role) - [Update a role](/en/docs/openapi/role-definitions/update_role): Update role definition name and description. Requires `roles.manage` permission. - System - [Get public configuration for a realm](/en/docs/openapi/system/get_public_config): Returns publicly accessible configuration for the specified realm, including: - Registration settings (whether registration is allowed and if email verification is required) - List of enabled OAuth providers - Published white-label settings - Whether the realm has a usable email channel This endpoint does not require authentication. - [Health check endpoint for monitoring and orchestration](/en/docs/openapi/system/health_check): Used by: - Kubernetes liveness probes (is the service running?) - Kubernetes readiness probes (can the service handle traffic?) - Monitoring systems (Prometheus, Datadog, etc.) - Load balancers (health checks) # Health Criteria The service is considered **healthy** when: - PostgreSQL database is reachable (`SELECT 1` succeeds) - Redis cache is reachable (PING succeeds) # Response Codes - **200 OK**: Service is healthy - **503 Service Unavailable**: Service is unhealthy - [Resolve the current request host to a published custom-domain realm.](/en/docs/openapi/system/resolve_custom_domain): Used by the SPA on custom domains before it knows the realm id. This endpoint is intentionally separate from the internal Caddy ask endpoint: the ask endpoint stays shared-secret protected and never leaks realm identity, while this public endpoint is only for hosts already serving the app. - User - [Change password for current user](/en/docs/openapi/user/change_user_password) - [Self-service account deletion (soft-delete).](/en/docs/openapi/user/delete_account): Requires an authenticated browser token. Anonymizes PII, marks the account `Deleted`, clears TOTP, cancels in-effect subscriptions, revokes all sessions (incl. the caller's), and records a `user.delete` audit event. One-time purchases are NOT refunded. Returns 204 on success — the caller's token is invalidated by the session wipe. - [Get the current user's permission list](/en/docs/openapi/user/get_current_user_permissions): Returns the list of all permission names the current user has, used for frontend menu permission control. **has_all field**: Legacy response field kept for compatibility. The user endpoint no longer expands the full set of realm permission definitions and always returns false. - [Get current user profile](/en/docs/openapi/user/get_profile) - [Get_user_feature_availability](/en/docs/openapi/user/get_user_feature_availability) - [Get the current user's roles and permissions](/en/docs/openapi/user/get_user_roles): Returns all roles (role names) and permissions (permission names) of the currently logged-in user. Returns the roles and permissions the user actually holds, without expanding the full set of realm permission definitions. - [Handle_begin_passkey_registration](/en/docs/openapi/user/handle_begin_passkey_registration) - [Handle_begin_reauth](/en/docs/openapi/user/handle_begin_reauth) - [Handle_delete_passkey_credential](/en/docs/openapi/user/handle_delete_passkey_credential) - [Disable TOTP two-factor authentication for the current user](/en/docs/openapi/user/handle_disable_totp): Disables TOTP and removes the configuration. Requires a single-use reauth ticket obtained by verifying any one of the user's factors (password, TOTP, or Passkey) via `POST /api/user/reauth/verify` (see `consume_reauth`). Cannot be disabled if the realm has force-enabled TOTP. - [Enable TOTP two-factor authentication for the current user](/en/docs/openapi/user/handle_enable_totp): Initiates TOTP setup by generating a secret key, QR code URL, and backup codes. The user must verify the TOTP code to complete the setup. Requires a single-use reauth ticket obtained by verifying any one of the user's factors (password, TOTP, or Passkey) via `POST /api/user/reauth/verify` (see `consume_reauth`). - [Handle_finish_passkey_registration](/en/docs/openapi/user/handle_finish_passkey_registration) - [Get TOTP configuration status for the current user](/en/docs/openapi/user/handle_get_totp_status): Returns the TOTP status including whether it is enabled, when it was enabled, and backup code usage statistics. - [Handle_list_passkey_credentials](/en/docs/openapi/user/handle_list_passkey_credentials) - [Regenerate TOTP secret and backup codes](/en/docs/openapi/user/handle_regenerate_totp): Generates a new TOTP secret and backup codes, staged for swap: the old secret and backup codes remain valid until the user verifies a code from the new secret; abandoning the flow (or a failed verification) leaves them intact, so 2FA is never silently disabled. Requires a single-use reauth ticket obtained by verifying any one of the user's factors (password, TOTP, or Passkey) via `POST /api/user/reauth/verify` (see `consume_reauth`). - [Handle_rename_passkey_credential](/en/docs/openapi/user/handle_rename_passkey_credential) - [Handle_verify_reauth](/en/docs/openapi/user/handle_verify_reauth) - [Verify TOTP setup and enable two-factor authentication](/en/docs/openapi/user/handle_verify_totp_setup): Completes the TOTP setup process by verifying the TOTP code generated from the secret. On successful verification, TOTP is enabled for the user account. - [List_user_transactions](/en/docs/openapi/user/list_user_transactions) - [List_user_wallets](/en/docs/openapi/user/list_user_wallets) - [Update current user profile](/en/docs/openapi/user/update_profile) - Users - [Get user roles](/en/docs/openapi/users/adminGetUserRoles): Returns the list of roles assigned to a specific user. - [Assign direct permission to user](/en/docs/openapi/users/assign_user_permission): Assigns a permission directly to a user (bypassing roles). **Security constraints**: - Requires policies.manage permission - Cannot create "All" or wildcard policies (privileged policies) - [Create a new user](/en/docs/openapi/users/create_user): Create a new user with email, password, and optional roles. Requires `users.manage` permission. Realm-admins can only assign the 'user' role unless they also have `roles.manage` permission. - [Delete a user](/en/docs/openapi/users/delete_user): Delete a user from the realm. Requires `users.manage` permission. - [Get user's effective permissions](/en/docs/openapi/users/get_effective_permissions): Returns all permissions a user has, including: - Permissions directly assigned to the user - Permissions inherited from roles Each permission includes its source (role name or "direct"). - [Get user's directly assigned permissions](/en/docs/openapi/users/get_user_permissions): Returns permissions that were directly assigned to the user (not through roles). These are policies where the subject is the user_id itself. **Note**: This endpoint returns only direct permissions. For effective permissions (including those inherited from roles), use `/effective-permissions`. - [Get user by ID](/en/docs/openapi/users/get_user): Get detailed information about a specific user. Requires `users.view` permission. Cross-realm targets return 404 (indistinguishable from missing). - [List a user's active sessions](/en/docs/openapi/users/list_user_sessions): List active browser-token sessions for a specific user. Requires `users.manage` permission. - [List users in the realm](/en/docs/openapi/users/list_users): List users with pagination. Requires `users.view` permission. - [Remove direct permission from user](/en/docs/openapi/users/remove_user_permission): Removes a directly assigned permission from a user. - [Reset user password (generate 16-character random password)](/en/docs/openapi/users/reset_user_password) - [Revoke all sessions for a user](/en/docs/openapi/users/revoke_all_user_sessions): Revoke every active browser-token session for a user. Requires `users.manage` permission. - [Revoke a single user session](/en/docs/openapi/users/revoke_user_session): Revoke a single browser-token session family for a user. Requires `users.manage` permission. - [Update user roles](/en/docs/openapi/users/update_user_roles): Updates the roles assigned to a user. Replaces all existing roles with the new list. **Security constraints**: - Realm-admin can only assign "user" role, not "realm-admin" - [Update a user](/en/docs/openapi/users/update_user): Update user status or nickname. Email is read-only after creation. Requires `users.manage` permission. # 教程 - [快速上手](/zh/docs/getting-started) - [创建 Realm 并完成基础配置](/zh/docs/realm-setup): 在托管实例 auth.fornetcode.com 上创建自己的 realm,然后完成每个新 realm 都要做的三件事:开启注册、发布自己的用户协议与隐私政策、给 auth 页面做品牌化。 - [架构](/zh/docs/architecture): Rust 后端(六边形架构)+ React 前端(TanStack 全家桶),单体部署,所有组件跑在一个进程里。 - [配置](/zh/docs/configuration): Herald 通过一个 TOML 文件管理所有运行时配置。应用启动时读取该文件,不支持热加载——修改配置需要重启进程。 - [部署](/zh/docs/deployment): 用 docker compose 在一台服务器上跑起 Herald,三个容器搞定后端、数据库和缓存。 - 登录方式 - [Passkey 认证](/zh/docs/auth/passkey): Herald 的 Passkey 基于 WebAuthn / FIDO2。同一个凭证既能作为无密码的第一因素(conditional UI),也能在密码登录后作为第二因素。密码和 TOTP 始终保留为回退方式,设备丢失或浏览器不支持都不会让用户被锁死。 - [邮箱验证码登录](/zh/docs/auth/email-otp): Herald 的邮箱验证码登录让用户输入发送到邮箱的一次性验证码即可登录,不需要密码。Realm 管理员在 Settings 中开启,并可以设置让全新邮箱在首次登录时自动注册。验证码有效期短、尝试次数有限,并按邮箱和按 IP 限速。密码、TOTP、Passkey 仍然作为备选保留。 - [Apple native 登录(iOS App 内)](/zh/docs/auth/apple-native): 接入方的 iOS App 用 ASAuthorizationAppleIDProvider 拿到 Apple 签发的 identityToken,POST 给 Herald 校验,全程不离开 App。Herald 只校验凭证、不调 Apple token 端点、不接触 client_secret。支持直接会话和下游 Code+PKCE 两种分支,与 Apple web 跳转登录关联同一个 Herald 账号。 - [LDAP 企业目录登录](/zh/docs/auth/ldap): Herald 的 LDAP 登录让员工用企业目录(Active Directory、OpenLDAP 等 LDAPv3 目录)里已有的账号密码登录。Realm 管理员在 Settings 中配置目录连接后,登录页出现企业账号登录入口,首次登录自动创建没有本地密码的账号。目录只做凭据权威:Herald 不回写、不同步目录。 - 计费 - [Herald 计费架构](/zh/docs/billing/overview): Herald 的计费系统不维护本地商品目录。Stripe、Creem 这些支付方管理 Product、Price、Checkout 和订阅生命周期;Herald 只保留做授权和积分发放所需的最小数据——刚好够支撑一面与每笔支付、退款自动同步的付费墙。 - [Stripe 支付对接](/zh/docs/billing/stripe-payment): 面向 Realm Admin 的 Stripe 对接操作指南。跟着做完,你的用户就能通过 Stripe 完成订阅支付和积分包购买。 - [Creem 支付对接](/zh/docs/billing/creem-payment): 面向 Realm Admin 的 Creem 对接操作指南。跟着做完,你的用户就能通过 Creem 完成订阅支付。 - [WeChat Pay 对接](/zh/docs/billing/wechat-payment): 面向 Realm Admin 的 WeChat Pay 对接指南。配置商户凭据、创建按订单定价的映射,通过 PC 扫码(Native)或微信内唤起(JSAPI)收款。 - [App Store / Google Play 内购(IAP)对接](/zh/docs/billing/iap): 面向 Realm Admin 的 IAP 对接操作指南,涵盖订阅、积分包、买断和非续期订阅。 - [钱包支付(Google Pay / Apple Pay)](/zh/docs/billing/wallet-payments): 在既有 Stripe 与 Creem 收款链路上启用 Google Pay 和 Apple Pay。Web 端钱包出现在托管收银台;移动 App 声明 PaymentIntent 流并用原生钱包 SDK 完成确认。 - [多货币定价](/zh/docs/billing/multi-currency): 让一个 Stripe 产品以多种货币销售。覆盖购买页货币分组与显式选择,以及面向第三方应用的 api-ext 按货币解析接口。 - [发票管理](/zh/docs/billing/invoice): Herald 支持两种发票:管理员手动创建的发票,以及用户自助申请的发票。两种发票走同一个底层数据模型,但入口和操作权限不同。 - [Credit Note(发票贷记凭证)](/zh/docs/billing/credit-note): Herald 的发票系统支持三种来源:Stripe 外部同步、Creem MoR 同步、Herald 自研(manual)。Credit Note 用来在发票层面记录退款凭证,让发票的"已退款金额"和"剩余应付"与真实收款保持一致。 - 积分 - [积分账户(Credit Account)](/zh/docs/points/account): 积分账户是 Realm 内积分的隔离单元:每个账户持有独立余额、覆盖一组 Client App,只有被覆盖的应用才能消费。积分按来源分五种类型入账,可用余额由逐笔发放的账本派生——你看到的余额永远就是你能花的余额。 - [积分消耗](/zh/docs/points/consumption): 你的后端用一个原子调用花积分:POST /api/ext/points/{realmId}/consume。Herald 把这次消费路由到覆盖该 Client App 的积分账户,先扣滚动配额窗口、再按最近过期优先扣池余额,要么全部入账、要么整体回滚——永远不存在部分扣减。 - [积分配额(滚动窗口)](/zh/docs/points/quota): 为订阅积分和免费周期积分配置滚动窗口配额。一个 entitlement 可以带多个窗口,如 {5h: 500, week: 5000, month: 20000};任意时刻的可用配额是所有窗口剩余量的最小值。 - [支付墙配置(支付授予角色)](/zh/docs/points/paywall): 在 Entitlement Mapping 上配置「授予角色」就是 Herald 的支付墙:支付成功自动授角色,取消和全额退款自动回收(部分退款保留买断角色),你的应用用现有 RBAC 判断放行。 - 应用集成 - [第三方后端服务对接 Herald](/zh/docs/integration/third-party-backend): Herald 处理用户、登录、权限、计费,并签发 Bearer access/refresh token。你的后端只写业务逻辑,通过 Herald SDK 调用认证和计费接口。 - [Custom User UI(自建终端用户前端)](/zh/docs/integration/custom-user-ui): 集成方可以在自己的 origin 上自建整套终端用户前端——注册、登录、账户中心——而不需要自己的后端。前端通过 /login 端点拿到浏览器持有的 Bearer token 直接调用 /api/user/*。这是自建 UI 路径,区别于 White-label 品牌化,也区别于后端中转的 OAuth(BFF)路径。 - [MCP 集成(AI Agent 接入)](/zh/docs/integration/mcp): 用 OAuth 把 Claude Code、VS Code 等 MCP 客户端接入 Herald 租户——无需 API Key、无需 client secret。浏览器登录一次,即可通过九个只读工具查询你的租户。 - [OpenID Connect 集成](/zh/docs/integration/oidc): 用一个 issuer URL 把 Grafana、Vault 等标准 OIDC 客户端接入 Herald 租户——discovery、RS256 id_token 与 userinfo 叠加在既有 OAuth 2.0 授权码 + PKCE 流程之上,无需 Herald SDK。 - 品牌与域名 - [White-label(Realm 品牌化)](/zh/docs/customization/branding): Herald 允许 Realm 管理员对托管的 auth 页面做品牌化——logo、主色、背景、页脚文案、登录/注册页文案——让终端用户看到的是租户自己的品牌而不是默认的 Herald 外观。品牌化按 Realm 隔离,覆盖所有 auth 流页面及其子状态,任一资产缺失或失效都回退到 Herald 默认值。 - [自定义域名](/zh/docs/customization/custom-domain): Realm 管理员可以为本 Realm 注册一个品牌登录域名(如 login.acme.com),获取 CNAME 指引,并查看 CNAME/TLS 生效状态。域名全局唯一,保存即生效——没有单独的发布步骤。Herald 只授权反代层为已由 Realm 管理员注册的域名签发 TLS 证书。 - Openapi - Api keys - [Get API Key roles](/zh/docs/openapi/api-keys/adminGetApiKeyRoles): Returns the list of roles assigned to a specific API Key. - [Update API Key roles](/zh/docs/openapi/api-keys/adminUpdateApiKeyRoles): Replaces all roles assigned to an API Key. Empty array clears all roles. Builtin roles cannot be assigned to API Keys (returns 400). - [Create a new API Key](/zh/docs/openapi/api-keys/create_api_key): Generates a new API key for the specified realm. The plaintext key is returned only in this response and cannot be retrieved later. - [Delete an API Key](/zh/docs/openapi/api-keys/delete_api_key): Permanently deletes an API key. - [Get a specific API Key by ID](/zh/docs/openapi/api-keys/get_api_key): Retrieves details of an API key. Hash and plaintext are never exposed. - [List all API Keys for a realm](/zh/docs/openapi/api-keys/list_api_keys): Returns a paginated list of API keys. Hash and plaintext are never exposed. - [Rotate an API Key](/zh/docs/openapi/api-keys/rotate_api_key): Generates a new key for the API key, replacing the old hash. The old key is immediately invalidated. Returns the new plaintext key (shown once). - [Update an API Key](/zh/docs/openapi/api-keys/update_api_key): Partially updates an API key's name, enabled status, or expiration. - Audit - [Get a single audit event by ID](/zh/docs/openapi/audit/get_audit_event) - [List audit events with optional filters and pagination](/zh/docs/openapi/audit/list_audit_events) - Auth - [Confirm email change with verification code](/zh/docs/openapi/auth/change_email_confirm): Completes the email change using the code that arrived via the frontend confirm-page link in the confirmation email; the frontend calls this endpoint with the user's current session, which must be the code owner. - [Request to change the email address for the authenticated user](/zh/docs/openapi/auth/change_email_request): Sends a confirmation link to the new email address. The link points to the first-party frontend confirm page, which the user must open in their logged-in browser so the frontend can call the confirm endpoint with the current session. Returns 400 when the realm has no usable email channel. - [Get registration configuration status for a realm](/zh/docs/openapi/auth/get_registration_status): Returns whether user registration is enabled for the specified realm. - [Public visibility of the self-service entry. Fail-closed: a missing toggle row resolves to `enabled: false`.](/zh/docs/openapi/auth/get_signup_status) - [Get Turnstile configuration status for a Client App](/zh/docs/openapi/auth/get_turnstile_status): Returns whether Turnstile verification is enabled for the specified Client App and the site key to render the widget. Turnstile is fully delegated to the Client App (D-PROTECT-01); the legacy realm_config source is no longer read. A missing or disabled Client App is rejected with 401, matching the other anonymous Client-App-bound endpoints. Pass `clientId` as a query parameter. - [Handle_passkey_2fa_options](/zh/docs/openapi/auth/handle_passkey_2fa_options) - [Handle_passkey_2fa_verify](/zh/docs/openapi/auth/handle_passkey_2fa_verify) - [Handle_passkey_options](/zh/docs/openapi/auth/handle_passkey_options) - [Handle_passkey_verify](/zh/docs/openapi/auth/handle_passkey_verify) - [Verify TOTP code for two-factor authentication](/zh/docs/openapi/auth/handle_verify_totp): Completes the login process for users with TOTP enabled. Accepts either a TOTP code from an authenticator app or a backup code. Creates a permanent session on success. - [Enterprise (LDAP) directory login.](/zh/docs/openapi/auth/ldap_login): Authenticates the submitted username/password against the Realm's configured directory (search-then-bind), matches or JIT-provisions the Herald account, and then runs the full existing login pipeline (2FA / consent / OAuth code / session issuance), auditing `method="ldap"`. - [Public LDAP enablement flag for a Realm (login-page entry visibility). Fail-closed: absent/malformed/disabled/insecure-channel config → false.](/zh/docs/openapi/auth/ldap_status) - [Authenticate user with email/username and password](/zh/docs/openapi/auth/login): Supports both email and username login. Returns a session token or requires TOTP verification if two-factor authentication is enabled for the user. - [Logout](/zh/docs/openapi/auth/logout) - [Refresh](/zh/docs/openapi/auth/refresh) - [Register new user account](/zh/docs/openapi/auth/register): Creates a new user account with email and optional username. If email verification is required for realm, account will be created in a pending state and a verification email will be sent. - [Confirm password reset with verification code](/zh/docs/openapi/auth/reset_password_confirm): Completes password reset process using verification code sent to user's email. - [Request password reset](/zh/docs/openapi/auth/reset_password_request): Initiates a password reset process by sending a verification code to user's email address. Always returns success to prevent email enumeration. - [Email the 6-digit verification code the signup flow will later consume.](/zh/docs/openapi/auth/send_email_code): Only the admin realm hosts this entry; any other `realmId` is rejected. No Turnstile here (same posture as the other code-only senders — the per-IP + per-mailbox rate limits carry the anti-bombing defense), and the platform toggle gates it fail-closed so the code sender can never outlive the signup entry it serves. - [Send an email OTP login code.](/zh/docs/openapi/auth/send): Issues a one-time verification code for an existing active user, or — if the Realm has OTP auto-register enabled — for an unregistered email after consent is expressed. Partially enumeration-resistant: a 200 is returned for a known but non-active user (no code sent). An explicit 409 is still returned for an unregistered email when auto-register is off — the same registration-status bit the register endpoint exposes by design (both paths sit behind Turnstile and per-email/IP rate limits). - [Provision a new realm from an unauthenticated visitor and issue an immediate first-party admin-console session for the new realm admin.](/zh/docs/openapi/auth/signup): Only the admin realm hosts this entry; any other `realmId` is rejected. Pre-flight defenses run before any realm is created: the platform toggle, human verification bound to the admin realm's `admin-web-console` Client App, and a same-IP 24h quota. - [Public passkey enablement flag for a Realm.](/zh/docs/openapi/auth/status): Reads the `passkey` / `settings` config row and returns `{ enabled }`. The login page uses this to decide whether to render the passkey entry before any passkey endpoint is called, rather than discovering disablement via a 404 from `POST /login/passkey/options`. Anonymous (no authentication). - [Switch_client](/zh/docs/openapi/auth/switch_client) - [Confirm email verification with code](/zh/docs/openapi/auth/verify_email_confirm): Completes email verification process using verification code sent to user's email. Activates user account upon successful verification. - [Trigger email verification](/zh/docs/openapi/auth/verify_email_trigger): Sends a verification email to specified email address with a confirmation link. Used to initiate email verification for pending accounts. - [Verify an email OTP login code and issue a session.](/zh/docs/openapi/auth/verify): On a matching code: consumes it (one-time), then either logs the existing user in (login-as-consent gate) or auto-registers a new account (create-without-password → activate → register-as-consent) and issues a Bearer token family via `RedisBrowserTokenService`. Returns `BrowserTokenResponse`. - Billing - [Batch-save all price mappings for a product.](/zh/docs/openapi/billing/batch_update_entitlement_mappings): Validation/permission order: 1. `billing.manage` (realm boundary + business permission). 2. If any update row carries a credit-strategy field → `points.manage`. 3. Credit-strategy field validation. Then the repository performs a single-transaction upsert of all the product's price rows: any row transitioning enabled true→false while protected by an active subscription rolls back the WHOLE transaction (409 with `{ activeSubscriptions }`). Cross-realm/product `mapping_id` tampering surfaces as 400. - [Cancel_payment_attempt](/zh/docs/openapi/billing/cancel_payment_attempt) - [Cancel the caller's own subscription for a client app.](/zh/docs/openapi/billing/cancel_subscription_for_client_app): Unlike the previous admin-only flip, this handler is a **user self-service** endpoint mounted on the browser-token router. It calls the **payment provider cancel API** (Stripe/Creem) and does **NOT** touch the local subscription row — local status is updated exclusively by the subsequent provider webhook (`customer.subscription.deleted` / `subscription.canceled`). If the provider call fails the error is surfaced to the caller and the local state is left untouched. Apple / Google in-app purchases cannot be canceled via a developer API (only via the App Store / Play Store by the user); this endpoint rejects them with 400. - [Create a Credit Bucket.](/zh/docs/openapi/billing/create_credit_bucket_handler) - [Create_entitlement_mapping](/zh/docs/openapi/billing/create_entitlement_mapping): Generic over provider (IAP, Stripe, Creem). Required permission: `billing.manage`; distribution-rule fields additionally require `points.manage` (mirrors the batch update permission model). A duplicate `(realm, provider, product, price)` row violates `uq_pem_realm_provider_product_price` and surfaces as HTTP 409. - [Create_payment_attempt](/zh/docs/openapi/billing/create_payment_attempt) - [Delete a Credit Bucket (DELETE).](/zh/docs/openapi/billing/delete_credit_bucket_handler): 204 on success; 409 `bucket_in_use` with `{ code, activeSubscriptions, holdersWithBalance, ruleReferences, historyReferences }` when in-flight subscriptions, residual balances, rule references or history exist. - [Fulfill_payment](/zh/docs/openapi/billing/fulfill_payment) - [Get the bucket overview matrix.](/zh/docs/openapi/billing/get_bucket_overview_handler): Returns `{ rows: OverviewRow[], grandTotal: ByCreditType }` — `grandTotal` is a SEPARATE top-level field, not appended to rows. - [Get a single Credit Bucket with coverage set and attached mappings.](/zh/docs/openapi/billing/get_credit_bucket_handler) - [Get a single entitlement mapping](/zh/docs/openapi/billing/get_entitlement_mapping) - [Get current user's subscription history for a specific subscription](/zh/docs/openapi/billing/get_my_subscription_history) - [Get_payment_attempt_status](/zh/docs/openapi/billing/get_payment_attempt_status) - [Payment statistics for the admin statistics page.](/zh/docs/openapi/billing/get_payment_stats): Counts only finalized attempts (Succeeded/Failed) attributed to their initiation day (`created_at`). Requires the `billing.view` permission. - [Get_purchase_history](/zh/docs/openapi/billing/get_purchase_history) - [Get_realm_purchase_history](/zh/docs/openapi/billing/get_realm_purchase_history) - [Get subscription for a client app](/zh/docs/openapi/billing/get_subscription_for_client_app) - [Get subscription history for a specific subscription](/zh/docs/openapi/billing/get_subscription_history) - [Get a specific subscription](/zh/docs/openapi/billing/get_subscription) - [Handle_apple_webhook](/zh/docs/openapi/billing/handle_apple_webhook) - [List all Credit Buckets for a realm.](/zh/docs/openapi/billing/list_credit_buckets_handler) - [List entitlement mappings for a realm](/zh/docs/openapi/billing/list_entitlement_mappings) - [List current user's subscription history with filtering and pagination](/zh/docs/openapi/billing/list_my_subscription_history) - [List enabled one-time entitlement mappings for a realm](/zh/docs/openapi/billing/list_one_time_mappings) - [List purchasable price-level options for a client app.](/zh/docs/openapi/billing/list_purchase_options): Returns a FLAT list of enabled price-granularity mappings (recurring + one_time) for the purchase page; the frontend groups by `external_product_id` / billing period. Replaces the purchase page's dependency on `list_one_time_mappings` (which only covered one_time). - [List subscription history with filtering and pagination](/zh/docs/openapi/billing/list_subscription_history) - [List subscriptions for a realm](/zh/docs/openapi/billing/list_subscriptions) - [Submit_iap_receipt](/zh/docs/openapi/billing/submit_iap_receipt) - [Sync provider products into entitlement mappings](/zh/docs/openapi/billing/sync_provider_products) - [Update a Credit Bucket (PUT). Coverage set is fully replaced.](/zh/docs/openapi/billing/update_credit_bucket_handler) - [Update an entitlement mapping](/zh/docs/openapi/billing/update_entitlement_mapping) - Billing invoice - [Apply_invoice](/zh/docs/openapi/billing-invoice/apply_invoice) - [Create_credit_note](/zh/docs/openapi/billing-invoice/create_credit_note) - [Create_invoice](/zh/docs/openapi/billing-invoice/create_invoice) - [Download_invoice_pdf](/zh/docs/openapi/billing-invoice/download_invoice_pdf) - [Download_my_invoice_pdf](/zh/docs/openapi/billing-invoice/download_my_invoice_pdf) - [Per-resource invoice apply-eligibility (read-only, context-level).](/zh/docs/openapi/billing-invoice/get_invoice_apply_eligibility): Decision order: ownership → provider → policy → seller config → external-invoice-exists, then delegates the verdict to the pure `determine_invoice_apply_route`. The endpoint resolves facts; the pure function owns the rules (single home — avoids divergence from the write path `validate_invoice_creation_policy` / `apply_invoice`). - [Get_invoice](/zh/docs/openapi/billing-invoice/get_invoice) - [Get_my_invoice_scoped](/zh/docs/openapi/billing-invoice/get_my_invoice_scoped) - [Get_seller_config](/zh/docs/openapi/billing-invoice/get_seller_config) - [Issue_invoice](/zh/docs/openapi/billing-invoice/issue_invoice) - [Admin read-only attribution anomaly discovery.](/zh/docs/openapi/billing-invoice/list_attribution_anomalies): Returns two anomaly classes: - `unattributed_invoices`: externally-synced invoices that lack both `subscription_id` and `payment_attempt_id` (reuses `list_admin` with `attribution=Missing`, capped at the first 100 rows). - `payments_without_invoice`: succeeded renewal / one-time payment attempts inside a 90-day window with no invoice linked via `invoice.payment_attempt_id` (anti-join read; no writes). Permission reuses `require_billing_permission(..., "view")` — same model as `GET /invoices`. Realm isolation is enforced both by the repository (`realm_id` filter) and the shared middleware. - [List_invoices](/zh/docs/openapi/billing-invoice/list_invoices) - [List_my_invoices](/zh/docs/openapi/billing-invoice/list_my_invoices) - [Mark_paid](/zh/docs/openapi/billing-invoice/mark_paid) - [Update_invoice](/zh/docs/openapi/billing-invoice/update_invoice) - [Upsert_seller_config](/zh/docs/openapi/billing-invoice/upsert_seller_config) - [Void_invoice](/zh/docs/openapi/billing-invoice/void_invoice) - Billing.payment providers - [List_payment_providers](/zh/docs/openapi/billing.payment-providers/list_payment_providers) - Client - [Create a new client app](/zh/docs/openapi/client/create_client_app): Creates a new OAuth client application with the specified configuration. - [Delete a client app](/zh/docs/openapi/client/delete_client_app): Deletes an OAuth client application and its associated roles. Built-in client apps (admin console, user account center, the realm's built-in API Key client app, and the built-in MCP client) cannot be deleted — disabling is the supported off-switch for the MCP client. - [Get a specific client app by ID](/zh/docs/openapi/client/get_client_app): Retrieves the details of a specific OAuth client application. - [List client applications](/zh/docs/openapi/client/list_client_apps): List all OAuth client applications configured for the specified realm with pagination. Requires `clients.view` permission. - [Update a client app configuration](/zh/docs/openapi/client/update_client_app): Updates the configuration of an existing OAuth client application. Optionally regenerate the client secret. - Dashboard - [Get dashboard statistics for a realm](/zh/docs/openapi/dashboard/get_dashboard_stats) - Device - [Device_authorize](/zh/docs/openapi/device/device_authorize) - [Device_confirm](/zh/docs/openapi/device/device_confirm) - [Device_token](/zh/docs/openapi/device/device_token) - [Device_verify](/zh/docs/openapi/device/device_verify) - Ext - [Check user permissions](/zh/docs/openapi/ext/check_permission): This endpoint allows third-party applications to check if a user has specific permissions using their browser access token. # Authentication Requires valid API Key via X-API-Key header # Example ```json { "accessToken": "user-access-token", "rules": [ {"resource": "article", "action": "read"}, {"resource": "article", "action": "write"} ] } ``` - [Consume points from user account](/zh/docs/openapi/ext/consume_points_ext): Consumes points from a user's account for paid operations. Returns transaction details including the new balance. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X POST \ https://api.example.com/api/ext/points/realm123/consume \ -H "X-API-Key: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "userId": "user-123", "clientAppId": "app-abc", "amount": 100, "description": "AI API call" }' ``` - [Create a new client app in a realm](/zh/docs/openapi/ext/create_client_app): Creates a client app in the specified realm. Only principals with `clients:manage` permission in the target realm may invoke this endpoint. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `clients:manage` permission in the target realm. - [Create a new realm](/zh/docs/openapi/ext/create_realm): Creates a new realm in the system. Only principals with `realm:manage` permission in the admin realm may invoke this endpoint. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `realm:manage` permission in the `admin` realm. - [Create a new user in a realm](/zh/docs/openapi/ext/create_user): Creates a user in the specified realm. Only principals with `users:create` permission in the target realm may invoke this endpoint. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `users:create` permission in the target realm. - [Get_balance_ext](/zh/docs/openapi/ext/get_balance_ext) - [Get a single client app by ID within a realm](/zh/docs/openapi/ext/get_client_app): Returns detailed information for the specified client app. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `clients:view` permission in the target realm. - [Get the currency set supported by a purchasable entitlement](/zh/docs/openapi/ext/get_entitlement_currencies): Returns the deduplicated currencies covered by the entitlement's enabled Stripe mapping rows (subscription-type and one-time-type rows both count). Creem/IAP rows are excluded: their pricing is provider/store-side and carries no per-currency price rows. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. - [Get purchasable one-time entitlement mappings](/zh/docs/openapi/ext/get_one_time_mappings): Returns enabled one-time entitlement mappings that have provider product info configured. Used by frontend/SDK to display purchasable products. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. - [Get a single realm by ID](/zh/docs/openapi/ext/get_realm): Returns detailed information for the specified realm. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `realm:view` permission in the target realm. Every principal may only view its own realm (no super-admin). - [Get subscription status for a client app](/zh/docs/openapi/ext/get_subscription): Returns subscription information for the specified client app. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the client app. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X GET \ https://api.example.com/api/ext/subscription/client-app-123 \ -H "X-API-Key: your-api-key" ``` - [Get transaction by external reference ID](/zh/docs/openapi/ext/get_transaction_by_external_ref_ext): Returns details of a specific points transaction by `externalRefId`. - [Get transaction by ID](/zh/docs/openapi/ext/get_transaction_ext): Returns details of a specific points transaction. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X GET \ https://api.example.com/api/ext/points/realm123/transactions/uuid \ -H "X-API-Key: your-api-key" ``` - [Get a single user by ID within a realm](/zh/docs/openapi/ext/get_user): Returns detailed information for the specified user. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `users:view` permission in the target realm. - [Grant points to a user account (SDK)](/zh/docs/openapi/ext/grant_points_ext): Grants points to a user's account from a third-party application. Returns transaction details including the new balance. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. Cross-realm requests will return 403 Forbidden. # Example ```bash curl -X POST \ https://api.example.com/api/ext/points/realm123/grant \ -H "X-API-Key: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "userId": "user-123", "amount": 100, "reason": "Promotional grant" }' ``` - [List client apps in a realm](/zh/docs/openapi/ext/list_client_apps): Returns all client apps in the specified realm. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `clients:view` permission in the target realm. - [List realms](/zh/docs/openapi/ext/list_realms): Returns the realms visible to the caller. Principals in the `admin` realm see all realms (platform-level enumeration); every other principal sees only its own realm. Realm-level detail endpoints remain strictly own-realm — this list is the only platform-wide view. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `realm:view` permission in their own realm. - [List users in a realm](/zh/docs/openapi/ext/list_users): Returns a paginated list of users in the specified realm. Default page_size is 20, maximum is 100. # Authentication Requires valid API Key via X-API-Key header # Authorization The caller must have `users:view` permission in the target realm. - [Resolve an entitlement's default price row by currency](/zh/docs/openapi/ext/resolve_default_price): Filters the entitlement's enabled Stripe mapping rows by the requested currency (plus optional billing type/period filters) and returns the unique match. Fail-loud by design: 404 when no row matches the currency (no secondary-currency fallback), 409 when multiple rows remain and the caller must narrow by billing type/period. # Authentication Requires valid API Key via X-API-Key header # Realm Isolation The API key must belong to the same realm as the requested realm. - Legal - [Discard the staged draft.](/zh/docs/openapi/legal/admin_discard_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Idempotent: discarding a missing draft returns 204. The published version table is untouched. - [Get the staged draft for an agreement type.](/zh/docs/openapi/legal/admin_get_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Returns 404 when no draft exists for the type (the admin form treats this as "start a new draft"). - [Get a single published version's full body by id (admin history view).](/zh/docs/openapi/legal/admin_get_version): Admin — requires first-party Bearer + `settings.view` (realm pinned by the session token) (same gate as `admin_list_agreements`, since this is a read of the same history the list already exposes). Returns 404 when the id does not resolve. The `realmId` path segment scopes the permission check; the version itself is looked up by primary key (history rows already surfaced by the list endpoint are guaranteed to belong to the realm or be the platform default). - [List both agreement types with their source, current effective version, and version history for the realm.](/zh/docs/openapi/legal/admin_list_agreements): Admin — requires first-party Bearer + `settings.view` + `has_access_to_realm`. `source` is derived per-type from `has_custom` (Custom when a realm custom version exists, otherwise Default). - [Publish a new per-realm custom agreement version.](/zh/docs/openapi/legal/admin_publish_custom): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Unknown `agreementType` → 400; non-object/empty `content` → 400 (also enforced in the service). On success the service records an `agreement.published` audit event and returns the new version. - [Publish the staged draft as a new effective version.](/zh/docs/openapi/legal/admin_publish_from_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Reads the draft, creates a new immutable `legal_agreement_version` row (advancing `version_no`, recording an `agreement.published` audit event), and clears the draft. Returns 404 when no draft exists for the type. This is the only path the admin UI uses to publish — there is no "publish without a draft" entry in the UI (the legacy `PUT /agreements/{type}` handler remains for backward compatibility). - [Revert a realm's agreement to the platform default.](/zh/docs/openapi/legal/admin_revert_to_default): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). Implemented as **default-follow semantics** in the service: a realm-scoped marker version is appended with `source = default` (new `version_id`, monotonic `version_no`), after which effective resolution follows the latest platform default rather than a frozen copy. History remains append-only; no prior rows are deleted. The handler is a thin pass-through. - [Save (upsert) a draft for an agreement type.](/zh/docs/openapi/legal/admin_save_draft): Admin — requires first-party Bearer + `settings.manage` (realm pinned by the session token). A repeat save overwrites the prior draft. Does NOT publish — the agreement stays unchanged for end users until POST `/publish`. - [Get the full localized detail of a single agreement type for a realm.](/zh/docs/openapi/legal/get_agreement): Public — no Bearer identity. Unknown `agreementType` → 400; no effective version deployed → 404. - [Reconsent gate verdict for the calling user across both agreement types.](/zh/docs/openapi/legal/get_consent_status): Self-service — requires Bearer identity. The realm is the one pinned by the bearer token (each user may only inspect their own realm's consent state). - [List the current effective agreements for a realm (Terms + Privacy).](/zh/docs/openapi/legal/list_agreements): Public — no Bearer identity. Each agreement type resolves to its current effective version (realm custom if present, otherwise platform default); missing types are skipped. Returns 404 when no type resolves, which signals a seed-missing deployment anomaly (the platform default templates should always be present). - [Record the user's explicit consent to one or more agreement versions.](/zh/docs/openapi/legal/record_consent): Self-service — requires Bearer identity; the realm is the one pinned by the bearer token. Each `version_id` must equal the current effective version for its type (enforced in the service layer, BE-D04); a stale version surfaces as 409 so the client re-reads the effective version. Returns 204 on success. The upsert is idempotent on a repeat of the same version. - Oauth - [Apple native login.](/zh/docs/openapi/oauth/apple_native_login): Verifies an Apple `identityToken` (from `ASAuthorizationAppleIDProvider`) server-side and, depending on `downstreamState`, issues either a direct browser session (Bearer token family bound to `clientId`) or a downstream authorization code for the Code+PKCE flow. Requires the realm to have an enabled Apple provider. - [Create OAuth provider configuration](/zh/docs/openapi/oauth/create_oauth_config) - [Delete OAuth provider configuration](/zh/docs/openapi/oauth/delete_oauth_config) - [Get OAuth provider configuration by provider type](/zh/docs/openapi/oauth/get_oauth_config) - [Google One Tap login.](/zh/docs/openapi/oauth/google_one_tap): Verifies a Google One Tap (GIS) ID Token server-side and, depending on `downstreamState`, issues either a direct browser session (Bearer token family bound to `clientId`) or a downstream authorization code for the Code+PKCE flow. Requires the realm to have an enabled Google provider. - [List all OAuth provider configurations for a realm](/zh/docs/openapi/oauth/list_oauth_configs) - [MCP dynamic client registration — gated pass-through form (DEC-mcp-server-007). Advertised from the AS metadata as `registration_endpoint` because mainstream MCP clients refuse to connect without one; after the gate (rate limit + RFC 7591 shape + loopback redirect validation) it returns the realm's preset public `herald-mcp` client and persists NOTHING — the abuse surface therefore equals the preset-client status quo (the real security boundaries stay in authorize's loopback whitelist, mandatory PKCE, and the tool-layer RBAC/scope gates).](/zh/docs/openapi/oauth/mcp_client_registration) - [RFC 9728 Protected Resource Metadata for a realm's MCP endpoint.](/zh/docs/openapi/oauth/mcp_protected_resource_metadata) - [RFC 8414 Authorization Server metadata for the per-realm OAuth issuer behind the MCP resource. Served at the standard path-style well-known insertion (well-known between host and issuer path); the legacy OIDC append-form discovery URL keeps working unchanged.](/zh/docs/openapi/oauth/oauth_authorization_server_metadata) - [OAuth authorize endpoint (Authorization Code + PKCE)](/zh/docs/openapi/oauth/oauth_authorize): Initiates the Authorization Code + PKCE flow: 1. Validates client_id exists and is enabled 2. Validates redirect_uri is in whitelist (exact match only) 3. Validates PKCE code_challenge_method (must be S256 if provided) 4. Stores state token with PKCE parameters in Redis (5 minutes TTL) 5. Redirects to login page with OAuth parameters # Arguments * `realm_id` - Realm identifier * `params` - OAuth query parameters (client_id, redirect_uri, state, response_type, code_challenge, code_challenge_method) # Returns * 302 redirect to login page with OAuth parameters # Errors * 400 - Invalid parameters (missing client_id, redirect_uri, state, or code_challenge) * 400 - Invalid response_type (must be "code") * 400 - Unsupported code_challenge_method (must be "S256") * 404 - Client not found * 403 - Client app is disabled * 400 - Redirect URI not in whitelist - [Handle OAuth callback submitted as an HTML form (Apple `response_mode=form_post` sends the authorization response as a POST body instead of a redirect query). Same semantics as the GET variant.](/zh/docs/openapi/oauth/oauth_callback_form) - [Handle OAuth callback from provider for a realm](/zh/docs/openapi/oauth/oauth_callback) - [Initiate OAuth login flow for a realm](/zh/docs/openapi/oauth/oauth_login) - [Oauth_token](/zh/docs/openapi/oauth/oauth_token) - [Oidc_discovery](/zh/docs/openapi/oauth/oidc_discovery) - [Oidc_jwks](/zh/docs/openapi/oauth/oidc_jwks) - [Oidc_rotate_signing_key](/zh/docs/openapi/oauth/oidc_rotate_signing_key) - [Oidc_userinfo](/zh/docs/openapi/oauth/oidc_userinfo) - [Update OAuth provider configuration](/zh/docs/openapi/oauth/update_oauth_config) - [Handle WeChat OAuth callback](/zh/docs/openapi/oauth/wechat_callback) - [Generate WeChat authorization URL (QRconnect)](/zh/docs/openapi/oauth/wechat_login) - [WeChat Mini Program login (code2session)](/zh/docs/openapi/oauth/wechat_miniprogram_login) - Permission - [Add policy to role](/zh/docs/openapi/permission/add_policy_to_role): Adds a permission policy to a role - [Assign roles to user](/zh/docs/openapi/permission/assign_roles_to_user): Assigns one or more roles to a user - [Permission check](/zh/docs/openapi/permission/check_permission): Spec: POST /api/permission/check - [Get role's policies](/zh/docs/openapi/permission/get_role_policies): Returns all permission policies for a specific role - [Get user's roles](/zh/docs/openapi/permission/permissionGetUserRoles): Returns all roles assigned to a specific user - [Remove policy from role](/zh/docs/openapi/permission/remove_policy_from_role): Removes a specific permission policy from a role - [Remove role from user](/zh/docs/openapi/permission/remove_role_from_user): Removes a specific role from a user - Permission definitions - [Create a new permission](/zh/docs/openapi/permission-definitions/create_permission_definition): Create a new permission definition. Permission name must be in format 'resource.action' (e.g., 'users.manage'). Requires `permissions.manage` permission. - [Delete a permission](/zh/docs/openapi/permission-definitions/delete_permission): Delete a permission definition. Built-in permissions cannot be deleted. Requires `permissions.manage` permission. - [Get permission by ID](/zh/docs/openapi/permission-definitions/get_permission) - [List permissions in the realm](/zh/docs/openapi/permission-definitions/list_permissions): List all permission definitions in the realm. Requires `permissions.view` permission. - [Update a permission](/zh/docs/openapi/permission-definitions/update_permission): Update permission definition. Built-in permissions cannot be modified. Requires `permissions.manage` permission. - Points - [Points consumption statistics for the admin statistics page.](/zh/docs/openapi/points/get_points_consumption_stats): Requires the `points.view` permission. Consumption is attributed to the transaction's creation day. - [Get the Realm's registration distribution rules.](/zh/docs/openapi/points/get_registration_rules) - [Get points wallet for a specific user](/zh/docs/openapi/points/get_wallet) - [Grant points to a user (admin)](/zh/docs/openapi/points/grant_points) - [List points transactions with filters](/zh/docs/openapi/points/list_transactions) - [List points wallets. Regular users see their own wallets; managers see all wallets in the realm.](/zh/docs/openapi/points/list_wallets) - [Update_wallet_status](/zh/docs/openapi/points/update_wallet_status) - [Atomically upsert the Realm's registration distribution rule set.](/zh/docs/openapi/points/upsert_registration_rules) - Realm_config - [Batch create or update configs](/zh/docs/openapi/realm_config/batch_upsert_realm_configs) - [Delete a config](/zh/docs/openapi/realm_config/delete_realm_config) - [Get email configuration status for a realm](/zh/docs/openapi/realm_config/email_status) - [Send a test email for a realm](/zh/docs/openapi/realm_config/email_test) - [Get a single config](/zh/docs/openapi/realm_config/get_realm_config) - [Get all configs of a given type](/zh/docs/openapi/realm_config/list_realm_configs_by_type) - [Get all configs for a given realm](/zh/docs/openapi/realm_config/list_realm_configs) - [Create or update a config (upsert)](/zh/docs/openapi/realm_config/upsert_realm_config) - Realms - [Create a new realm](/zh/docs/openapi/realms/create_realm) - [Get_feature_availability](/zh/docs/openapi/realms/get_feature_availability) - [Get realm by ID](/zh/docs/openapi/realms/get_realm) - [Caddy On-Demand TLS ask authorization endpoint.](/zh/docs/openapi/realms/handle_custom_domain_authorize): Returns `200 {"authorized": true}` when `host` matches a published+enabled `custom_domain_mapping` row; `404` on a miss (Caddy declines TLS issuance); `401` when the `X-Herald-Ask-Key` header is missing or mismatches the configured shared secret. Never exposes realm information — this is a certificate-abuse gate. - [Discard white-label draft.](/zh/docs/openapi/realms/handle_discard_white_label_draft) - [Get custom-domain management state.](/zh/docs/openapi/realms/handle_get_custom_domain_config) - [Get realm Email OTP configuration](/zh/docs/openapi/realms/handle_get_realm_email_otp_config): Retrieves the Email OTP configuration (login enabled + auto-register) for a realm. Requires 'settings.view' permission. Defaults to `{ enabled: false, auto_register: false }` when no config exists. - [Get realm Passkey configuration](/zh/docs/openapi/realms/handle_get_realm_passkey_config): Retrieves the Passkey configuration for a realm. Requires 'settings.view' permission. - [Get realm TOTP configuration and statistics](/zh/docs/openapi/realms/handle_get_realm_totp_config): Retrieves the TOTP configuration for a realm along with statistics about TOTP adoption including total users, enabled users, disabled users, and enablement rate. Requires 'settings.view' permission. - [Get white-label management state.](/zh/docs/openapi/realms/handle_get_white_label_config) - [Publish request body or the existing white-label draft.](/zh/docs/openapi/realms/handle_publish_white_label_config) - [Restore previous white-label settings.](/zh/docs/openapi/realms/handle_restore_white_label_config) - [Save white-label draft without publishing.](/zh/docs/openapi/realms/handle_save_white_label_draft) - [Update custom-domain configuration (writes the host→realm mapping).](/zh/docs/openapi/realms/handle_update_custom_domain_config) - [Update realm Email OTP configuration](/zh/docs/openapi/realms/handle_update_realm_email_otp_config): Enables/disables Email OTP login for a realm and separately controls whether unregistered emails are auto-registered on successful verification. Requires 'settings.manage' permission. Stored as `ConfigType::EmailOtp` / `config_key="settings"` JSON. - [Update realm Passkey configuration](/zh/docs/openapi/realms/handle_update_realm_passkey_config): Updates the Passkey configuration for a realm, including whether Passkey authentication is enabled. Requires 'settings.manage' permission. - [Update realm TOTP configuration](/zh/docs/openapi/realms/handle_update_realm_totp_config): Updates the TOTP (Time-based One-Time Password) configuration for a realm, including whether TOTP is enabled and whether it is force-required for all users. Requires 'settings.manage' permission. - [List realms with pagination](/zh/docs/openapi/realms/list_realms_paginated) - [List all realms (non-paginated, deprecated - use list_realms_paginated)](/zh/docs/openapi/realms/list_realms) - [Update realm](/zh/docs/openapi/realms/update_realm) - Role definitions - [Assign permission to role](/zh/docs/openapi/role-definitions/assign_permission_to_role) - [Create a new role](/zh/docs/openapi/role-definitions/create_role): Create a new role definition. Requires `roles.manage` permission. - [Delete a role](/zh/docs/openapi/role-definitions/delete_role): Delete a role definition. Built-in roles cannot be deleted. Requires `roles.manage` permission. - [Get permissions for a role](/zh/docs/openapi/role-definitions/get_role_permissions) - [Get role by ID](/zh/docs/openapi/role-definitions/get_role) - [List roles in the realm](/zh/docs/openapi/role-definitions/list_roles): List all role definitions for the admin-web-console client. Requires `roles.view` permission. - [Remove permission from role](/zh/docs/openapi/role-definitions/remove_permission_from_role) - [Update a role](/zh/docs/openapi/role-definitions/update_role): Update role definition name and description. Requires `roles.manage` permission. - System - [Get public configuration for a realm](/zh/docs/openapi/system/get_public_config): Returns publicly accessible configuration for the specified realm, including: - Registration settings (whether registration is allowed and if email verification is required) - List of enabled OAuth providers - Published white-label settings - Whether the realm has a usable email channel This endpoint does not require authentication. - [Health check endpoint for monitoring and orchestration](/zh/docs/openapi/system/health_check): Used by: - Kubernetes liveness probes (is the service running?) - Kubernetes readiness probes (can the service handle traffic?) - Monitoring systems (Prometheus, Datadog, etc.) - Load balancers (health checks) # Health Criteria The service is considered **healthy** when: - PostgreSQL database is reachable (`SELECT 1` succeeds) - Redis cache is reachable (PING succeeds) # Response Codes - **200 OK**: Service is healthy - **503 Service Unavailable**: Service is unhealthy - [Resolve the current request host to a published custom-domain realm.](/zh/docs/openapi/system/resolve_custom_domain): Used by the SPA on custom domains before it knows the realm id. This endpoint is intentionally separate from the internal Caddy ask endpoint: the ask endpoint stays shared-secret protected and never leaks realm identity, while this public endpoint is only for hosts already serving the app. - User - [Change password for current user](/zh/docs/openapi/user/change_user_password) - [Self-service account deletion (soft-delete).](/zh/docs/openapi/user/delete_account): Requires an authenticated browser token. Anonymizes PII, marks the account `Deleted`, clears TOTP, cancels in-effect subscriptions, revokes all sessions (incl. the caller's), and records a `user.delete` audit event. One-time purchases are NOT refunded. Returns 204 on success — the caller's token is invalidated by the session wipe. - [Get the current user's permission list](/zh/docs/openapi/user/get_current_user_permissions): Returns the list of all permission names the current user has, used for frontend menu permission control. **has_all field**: Legacy response field kept for compatibility. The user endpoint no longer expands the full set of realm permission definitions and always returns false. - [Get current user profile](/zh/docs/openapi/user/get_profile) - [Get_user_feature_availability](/zh/docs/openapi/user/get_user_feature_availability) - [Get the current user's roles and permissions](/zh/docs/openapi/user/get_user_roles): Returns all roles (role names) and permissions (permission names) of the currently logged-in user. Returns the roles and permissions the user actually holds, without expanding the full set of realm permission definitions. - [Handle_begin_passkey_registration](/zh/docs/openapi/user/handle_begin_passkey_registration) - [Handle_begin_reauth](/zh/docs/openapi/user/handle_begin_reauth) - [Handle_delete_passkey_credential](/zh/docs/openapi/user/handle_delete_passkey_credential) - [Disable TOTP two-factor authentication for the current user](/zh/docs/openapi/user/handle_disable_totp): Disables TOTP and removes the configuration. Requires a single-use reauth ticket obtained by verifying any one of the user's factors (password, TOTP, or Passkey) via `POST /api/user/reauth/verify` (see `consume_reauth`). Cannot be disabled if the realm has force-enabled TOTP. - [Enable TOTP two-factor authentication for the current user](/zh/docs/openapi/user/handle_enable_totp): Initiates TOTP setup by generating a secret key, QR code URL, and backup codes. The user must verify the TOTP code to complete the setup. Requires a single-use reauth ticket obtained by verifying any one of the user's factors (password, TOTP, or Passkey) via `POST /api/user/reauth/verify` (see `consume_reauth`). - [Handle_finish_passkey_registration](/zh/docs/openapi/user/handle_finish_passkey_registration) - [Get TOTP configuration status for the current user](/zh/docs/openapi/user/handle_get_totp_status): Returns the TOTP status including whether it is enabled, when it was enabled, and backup code usage statistics. - [Handle_list_passkey_credentials](/zh/docs/openapi/user/handle_list_passkey_credentials) - [Regenerate TOTP secret and backup codes](/zh/docs/openapi/user/handle_regenerate_totp): Generates a new TOTP secret and backup codes, staged for swap: the old secret and backup codes remain valid until the user verifies a code from the new secret; abandoning the flow (or a failed verification) leaves them intact, so 2FA is never silently disabled. Requires a single-use reauth ticket obtained by verifying any one of the user's factors (password, TOTP, or Passkey) via `POST /api/user/reauth/verify` (see `consume_reauth`). - [Handle_rename_passkey_credential](/zh/docs/openapi/user/handle_rename_passkey_credential) - [Handle_verify_reauth](/zh/docs/openapi/user/handle_verify_reauth) - [Verify TOTP setup and enable two-factor authentication](/zh/docs/openapi/user/handle_verify_totp_setup): Completes the TOTP setup process by verifying the TOTP code generated from the secret. On successful verification, TOTP is enabled for the user account. - [List_user_transactions](/zh/docs/openapi/user/list_user_transactions) - [List_user_wallets](/zh/docs/openapi/user/list_user_wallets) - [Update current user profile](/zh/docs/openapi/user/update_profile) - Users - [Get user roles](/zh/docs/openapi/users/adminGetUserRoles): Returns the list of roles assigned to a specific user. - [Assign direct permission to user](/zh/docs/openapi/users/assign_user_permission): Assigns a permission directly to a user (bypassing roles). **Security constraints**: - Requires policies.manage permission - Cannot create "All" or wildcard policies (privileged policies) - [Create a new user](/zh/docs/openapi/users/create_user): Create a new user with email, password, and optional roles. Requires `users.manage` permission. Realm-admins can only assign the 'user' role unless they also have `roles.manage` permission. - [Delete a user](/zh/docs/openapi/users/delete_user): Delete a user from the realm. Requires `users.manage` permission. - [Get user's effective permissions](/zh/docs/openapi/users/get_effective_permissions): Returns all permissions a user has, including: - Permissions directly assigned to the user - Permissions inherited from roles Each permission includes its source (role name or "direct"). - [Get user's directly assigned permissions](/zh/docs/openapi/users/get_user_permissions): Returns permissions that were directly assigned to the user (not through roles). These are policies where the subject is the user_id itself. **Note**: This endpoint returns only direct permissions. For effective permissions (including those inherited from roles), use `/effective-permissions`. - [Get user by ID](/zh/docs/openapi/users/get_user): Get detailed information about a specific user. Requires `users.view` permission. Cross-realm targets return 404 (indistinguishable from missing). - [List a user's active sessions](/zh/docs/openapi/users/list_user_sessions): List active browser-token sessions for a specific user. Requires `users.manage` permission. - [List users in the realm](/zh/docs/openapi/users/list_users): List users with pagination. Requires `users.view` permission. - [Remove direct permission from user](/zh/docs/openapi/users/remove_user_permission): Removes a directly assigned permission from a user. - [Reset user password (generate 16-character random password)](/zh/docs/openapi/users/reset_user_password) - [Revoke all sessions for a user](/zh/docs/openapi/users/revoke_all_user_sessions): Revoke every active browser-token session for a user. Requires `users.manage` permission. - [Revoke a single user session](/zh/docs/openapi/users/revoke_user_session): Revoke a single browser-token session family for a user. Requires `users.manage` permission. - [Update user roles](/zh/docs/openapi/users/update_user_roles): Updates the roles assigned to a user. Replaces all existing roles with the new list. **Security constraints**: - Realm-admin can only assign "user" role, not "realm-admin" - [Update a user](/zh/docs/openapi/users/update_user): Update user status or nickname. Email is read-only after creation. Requires `users.manage` permission.