# Authentication

The Rho API uses **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.

## Creating a token

API Access Tokens are created from your Rho banking settings. Token creation is protected by:

- The fact that only **Account Owners** and **Admins** can manage API Access Tokens.
- A **2FA challenge** at the moment of creation.


When you create a token you choose:

| Field | Notes |
|  --- | --- |
| **Name** | A human-readable label that appears in the token list |
| **Scopes** | One or more permissions the token grants (see [Scopes](#scopes)). |
| **Allowed IPs** | Optional IP allowlist. If set, requests from any other source IP are rejected. Up to 100 entries per token. |
| **Expiration** | Required. Maximum one year. |


The raw token is **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.

Tokens created in Rho banking settings use the `rhobat_` prefix, for example:

```
rhobat_4f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f
```

A business can hold at most **20 active tokens** at a time. If you need more, revoke unused ones first.

API Access Tokens also expire automatically after **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.

## Making an authenticated request

Send your token in the standard HTTP `Authorization` header using the `Bearer` scheme:

```http
GET /api/v1/transactions HTTP/1.1
Host: rhoapi.rho.co
Authorization: Bearer rhobat_4f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f
Accept: application/json
```

Or with `curl`:

```bash
curl https://rhoapi.rho.co/api/v1/transactions \
  -H "Authorization: Bearer $RHO_API_TOKEN"
```

No other authentication header (cookie, API key, signed request) is supported.

For sandbox requests to `https://rhoapi-sandbox.rho.co/api/v1`, any non-empty bearer token is accepted:

```bash
curl https://rhoapi-sandbox.rho.co/api/v1/transactions \
  -H "Authorization: Bearer sandbox"
```

Sandbox authentication is intentionally permissive so you can test API shapes against fictional data without creating a production API Access Token.

## Scopes

Scopes follow a `resource:action` format and are enforced before your request reaches the handler. A request whose token lacks the required scope is rejected with `403 Forbidden`.

The scopes available today are:

| Scope | Description |
|  --- | --- |
| `accounts:read` | Read account information for the business. |
| `transactions:read` | Read transactions the business has access to. |
| `statements:read` | Read statements for the business's accounts. |


The required scope for each endpoint is listed under its **Security** section in the [API reference](/api/v1/openapi).

## Error responses

Authentication and authorization failures are returned as [`application/problem+json`](https://www.rfc-editor.org/rfc/rfc7807) documents:

| Status | When it happens |
|  --- | --- |
| `401 Unauthorized` | Missing `Authorization` header, malformed token, unknown token, revoked token, or expired token. |
| `403 Forbidden` | 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. |


The body uses the standard problem-details shape:

```json
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Token is revoked or has expired"
}
```

## Revoking a token

Tokens can be revoked at any time from the same settings screen used to create them. Revocation is **immediate**: the next request made with that token will return `401 Unauthorized`. There is no grace period and no way to un-revoke - issue a new token instead.

If you believe a token has leaked, revoke it and notify Customer Support.

## Best practices

- **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.
- **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.
- **Grant the narrowest scopes** that the integration actually needs.
- **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.
- **Rotate before expiration.** Create the replacement token, deploy it, and only then revoke the old one to avoid downtime.