generated: '2026-09-03' method: searched source: >- https://www.wealthreader.com/docs/en/iframe-integration-2-of-2-backend.md ; https://www.wealthreader.com/docs/en/oauth-integration-backend.md ; openapi/wealth-reader-api-for-ai.yaml ; live response headers from https://api.wealthreader.com/entities/ docs: https://www.wealthreader.com/docs/en/oauth-integration-backend/ summary: >- Two-layer auth. The CLIENT authenticates to Wealth Reader with a static api_key. The END USER authenticates to their own bank inside Wealth Reader's widget or OAuth flow, and the result of that is a reusable `token` that Wealth Reader custodies on the client's behalf. The OpenAPI declares NO securitySchemes at all — the api_key travels as an ordinary form field — so nothing in the machine-readable contract tells a client generator that these calls are authenticated. spec_declares_security: false schemes: - id: api_key type: apiKey in: formData name: api_key transport: application/x-www-form-urlencoded request body applies_to: every operation except GET /entities/ and GET /error-codes/ (public catalogue reads) note: >- Also accepted as a query parameter on GET /cards/transactions/, where the spec itself warns "it travels in the query string, so it ends up in access logs and intermediary proxies". api.wealthreader.com advertises X-API-KEY in Access-Control-Allow-Headers, so a header form exists at the edge, but the published operations use the form field. obtained_from: signup plus an onboarding session with the technical team - id: bank_token type: custodied_credential name: token applies_to: 'POST /entities/, POST /tokens/revoke/, POST /tokens/reasign/' description: >- Opaque handle to the end user's custodied bank credential, returned in statistics.token the first time credentials are tokenized. Reusable for later refreshes; invalidated when the user changes their password or the bank raises a new second factor, which surfaces as error code 3 (blocked token) or 4 (temporarily invalid). lifecycle: issue: tokenize=1 on the widget or the domain registration revoke: 'POST /tokens/revoke/ (operationId revokeToken)' reassign: 'POST /tokens/reasign/ (operationId reasignToken) — moves a token between api_keys' list: 'POST /tokens/ (operationId queryTokensByApiKey), 500 per page' - id: oauth_pkce type: oauth2-like authorization_endpoint: https://oauth.wealthreader.com/oauth2/ token_endpoint: https://oauth.wealthreader.com/token/ grant: authorization_code with PKCE (S256) scopes: [] note: >- Shaped like OAuth 2.0 + PKCE — response_type=code, code_challenge_method=S256, state, nonce, redirect_uri, and a code exchanged at /token/ — but it is NOT standards-discoverable and it is not the standard flow. There is no /.well-known/oauth-authorization-server (probed 404 on both hosts) and no /.well-known/openid-configuration. No scopes are defined. Parameter names differ from RFC 7636: the challenge is sent as `challenge_code`, not `code_challenge`. The docs require the verifier to be a 41-character alphanumeric string hex-encoded to 82 characters, and `wr_conf` (the selector configuration JSON) is hex-encoded into the query string. Used when the integrator cannot embed the iframe — native apps and redirect flows. - id: webhook_signature type: hmac header: X-WR-Signature algorithm: 'sha256=' applies_to: real-time cards webhook deliveries secret_issuance: >- 64 hex characters, returned exactly once by POST /cards/webhook/ on first setup or when rotate_secret is true; otherwise it comes back null and cannot be retrieved again. verification: >- The provider explicitly instructs consumers to compare in constant time (hash_equals or equivalent) before trusting the body. - id: allwr_api_key type: apiKey product: ALL WR (allwr.io) — sibling platform, not the bank-aggregation API key_prefix: wrk_ scope: 'tasks:import' env: ALLWR_TOOLKIT_ALLWR_API_KEY source: https://raw.githubusercontent.com/Wealth-Reader/allwr-toolkit/main/docs/authentication.md note: >- The only scoped credential anywhere in the company's published surface. Created under Settings -> API keys in an ALL WR workspace. Recorded because it is the only place the provider documents a named permission scope. callback_auth: mechanism: none detail: >- The iframe callback POST to the integrator's own HTTPS endpoint carries no signature and no shared secret — only the operation_id the integrator generated. Authenticity rests on the integrator having registered that domain and callback URL in the client area beforehand, and on the operation_id being unguessable. The integrator must answer HTTP 200 with {"status":"ok"} or the widget will not report success to the frontend. Contrast with the cards webhook, which IS HMAC-signed. gaps: - The OpenAPI declares no securitySchemes, so generated clients get no auth wiring. - No OAuth discovery document; no scopes; non-standard PKCE parameter names. - The iframe callback is unsigned. - No /.well-known/openid-configuration or /.well-known/oauth-authorization-server (both 404).