{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Authentication","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"authentication","__idx":0},"children":["Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Rho API uses ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["API Access Tokens"]}," to authenticate requests. An API Access Token is a long-lived, opaque secret scoped to a single business - the same token continues to work as people on your team come and go."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"creating-a-token","__idx":1},"children":["Creating a token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["API Access Tokens are created from your Rho banking settings. Token creation is protected by:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The fact that only ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Account Owners"]}," and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Admins"]}," can manage API Access Tokens."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["2FA challenge"]}," at the moment of creation."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you create a token you choose:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Notes"},"children":["Notes"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Name"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A human-readable label that appears in the token list"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Scopes"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["One or more permissions the token grants (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#scopes"},"children":["Scopes"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Allowed IPs"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Optional IP allowlist. If set, requests from any other source IP are rejected. Up to 100 entries per token."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Expiration"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Required. Maximum one year."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The raw token is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["shown only once"]},", immediately after creation. There is no way to recover the secret later. Treat it like a password: copy it into your secret manager before closing the dialog, and never commit it to source control."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tokens created in Rho banking settings use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rhobat_"]}," prefix, for example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"rhobat_4f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A business can hold at most ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["20 active tokens"]}," at a time. If you need more, revoke unused ones first."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["API Access Tokens also expire automatically after ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["45 days of inactivity"]},". A successful authenticated API request counts as activity. For tokens that have never been used, the 45-day window starts at creation."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"making-an-authenticated-request","__idx":2},"children":["Making an authenticated request"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Send your token in the standard HTTP ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization"]}," header using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Bearer"]}," scheme:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"GET /api/v1/transactions HTTP/1.1\nHost: rhoapi.rho.co\nAuthorization: Bearer rhobat_4f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f\nAccept: application/json\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Or with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl https://rhoapi.rho.co/api/v1/transactions \\\n  -H \"Authorization: Bearer $RHO_API_TOKEN\"\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["No other authentication header (cookie, API key, signed request) is supported."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For sandbox requests to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://rhoapi-sandbox.rho.co/api/v1"]},", any non-empty bearer token is accepted:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl https://rhoapi-sandbox.rho.co/api/v1/transactions \\\n  -H \"Authorization: Bearer sandbox\"\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sandbox authentication is intentionally permissive so you can test API shapes against fictional data without creating a production API Access Token."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"scopes","__idx":3},"children":["Scopes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Scopes follow a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["resource:action"]}," format and are enforced before your request reaches the handler. A request whose token lacks the required scope is rejected with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["403 Forbidden"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The scopes available today are:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Scope"},"children":["Scope"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accounts:read"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Read account information for the business."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactions:read"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Read transactions the business has access to."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["statements:read"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Read statements for the business's accounts."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The required scope for each endpoint is listed under its ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Security"]}," section in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api/v1/openapi"},"children":["API reference"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"error-responses","__idx":4},"children":["Error responses"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Authentication and authorization failures are returned as ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.rfc-editor.org/rfc/rfc7807"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["application/problem+json"]}]}," documents:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Status"},"children":["Status"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"When it happens"},"children":["When it happens"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 Unauthorized"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Missing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization"]}," header, malformed token, unknown token, revoked token, or expired token."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["403 Forbidden"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Token is valid but does not carry the scope required by the endpoint, or the request came from an IP outside the token's allowlist."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The body uses the standard problem-details shape:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"about:blank\",\n  \"title\": \"Unauthorized\",\n  \"status\": 401,\n  \"detail\": \"Token is revoked or has expired\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"revoking-a-token","__idx":5},"children":["Revoking a token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tokens can be revoked at any time from the same settings screen used to create them. Revocation is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["immediate"]},": the next request made with that token will return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 Unauthorized"]},". There is no grace period and no way to un-revoke - issue a new token instead."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you believe a token has leaked, revoke it and notify Customer Support."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"best-practices","__idx":6},"children":["Best practices"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Store tokens in a secret manager"]}," (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, 1Password, etc.) - never in source control, CI logs, or shared documents."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Issue one token per integration."]}," Separate tokens make it possible to revoke a single integration without disturbing the others, and make audit logs easier to interpret."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Grant the narrowest scopes"]}," that the integration actually needs."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Set an IP allowlist"]}," when the calling system has a stable egress range. This blocks the token from being used outside your infrastructure even if the secret leaks."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Rotate before expiration."]}," Create the replacement token, deploy it, and only then revoke the old one to avoid downtime."]}]}]},"headings":[{"value":"Authentication","id":"authentication","depth":1},{"value":"Creating a token","id":"creating-a-token","depth":2},{"value":"Making an authenticated request","id":"making-an-authenticated-request","depth":2},{"value":"Scopes","id":"scopes","depth":2},{"value":"Error responses","id":"error-responses","depth":2},{"value":"Revoking a token","id":"revoking-a-token","depth":2},{"value":"Best practices","id":"best-practices","depth":2}],"frontmatter":{"seo":{"title":"Authentication"}},"lastModified":"2026-07-13T09:03:20.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/v1/auth","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}