Skip to content

Expenses

Beta

The Expenses API is in Beta. Additive changes are normal, and breaking changes may be made with notice while the resource incorporates customer feedback. See Versioning and compatibility.

These endpoints read the corporate expenses of the business linked to your API Access Token. They all require the expenses:read scope.

The Expenses API provides a read-only view of corporate card expenses. Reimbursements use their own API resource and are not returned by these endpoints. Each expense includes its approval state, policy compliance, tags, and receipt attachments.

List expenses

GET /api/v1/expenses returns expenses for the authenticated business in every approval state. Use the same filters and sorting options for every request in a paginated result set.

curl "https://rhoapi.rho.co/api/v1/expenses?approval_status=needs_approval&page_size=20" \
  -H "Authorization: Bearer $RHO_API_TOKEN"

Query parameters

ParameterDescription
approval_statusFilter by approval state. Repeat the key to pass several states (not_ready, automatically_approved, needs_approval, approved, rejected, archived).
user_idFilter by expense owner. Repeat the key to pass several user IDs.
transaction_date_afterEarliest spend date, inclusive, at midnight UTC (YYYY-MM-DD).
transaction_date_beforeLatest spend date, exclusive, at midnight UTC (YYYY-MM-DD).
created_afterEarliest creation timestamp, inclusive (date-time).
created_beforeLatest creation timestamp, exclusive (date-time).
has_attachmentstrue selects expenses with attachments; false selects those without.
sort_byField to sort on (created_at, transaction_date, amount). Defaults to created_at.
orderSort direction (asc, desc). Defaults to desc.
page_sizeNumber of expenses per page (1-100). Defaults to 20.
page_tokenOpaque cursor from the previous response.

Example response

{
  "expenses": [
    {
      "id": "0198ba3c-0000-7000-8000-000000000201",
      "amount": {"amount": 12500, "currency": "USD"},
      "transaction_date": "2026-09-08T14:30:00Z",
      "created_at": "2026-09-08T14:31:00Z",
      "updated_at": "2026-09-08T15:00:00Z",
      "user_id": "0198ba3c-0000-7000-8000-000000000101",
      "approval_status": "needs_approval",
      "merchant_name": "Delta Airlines",
      "merchant_category": "Airlines",
      "source_details": {
        "card_transaction": {
          "card_id": "0198ba3c-0000-7000-8000-000000000302",
          "transaction_type": "capture"
        }
      },
      "policy": {
        "requirements_status": "needs_details",
        "out_of_policy_status": "automatic_ok",
        "missing_requirements": [{"kind": "receipt"}]
      },
      "tags": [
        {"field_id": "0198ba3c-0000-7000-8000-000000000401", "field_name": "Department", "value": "Engineering"}
      ],
      "attachments": [
        {"file_id": "00000000-0000-0000-0000-0000000000f1", "file_name": "boarding_pass.pdf", "attached_at": "2026-09-08T14:35:00Z"}
      ]
    }
  ],
  "page": {"next_page_token": "opaque-cursor"}
}

page.next_page_token is null when there are no further pages. Page tokens belong to the API version that created them, so restart pagination when migrating from v0 to v1.

policy.requirements_status is either needs_details or requirements_met. policy.missing_requirements can be empty when requirement details are not available, including when the status is needs_details.

policy.out_of_policy_status preserves how the policy status was determined:

ValueMeaning
automatic_okAutomatically marked in policy.
automatic_not_okAutomatically marked out of policy.
manual_okManually marked in policy.
manual_not_okManually marked out of policy.

Get an expense

GET /api/v1/expenses/{expense_id} returns a single expense owned by the authenticated business. Missing and foreign-business expenses both return 404 Not Found without distinguishing between them.

curl "https://rhoapi.rho.co/api/v1/expenses/0198ba3c-0000-7000-8000-000000000201" \
  -H "Authorization: Bearer $RHO_API_TOKEN"

List expense activity

GET /api/v1/expenses/{expense_id}/activity returns an expense's audit history, including approval changes and field edits. Entries are ordered most recent first and use cursor pagination.

curl "https://rhoapi.rho.co/api/v1/expenses/0198ba3c-0000-7000-8000-000000000201/activity?page_size=50" \
  -H "Authorization: Bearer $RHO_API_TOKEN"
{
  "entries": [
    {
      "occurred_at": "2026-09-08T15:00:00Z",
      "actor_user_id": "0198ba3c-0000-7000-8000-000000000102",
      "automated": false,
      "changes": [
        {"field": "approval_status", "old_value": "NOT_READY", "new_value": "AUTOMATICALLY_APPROVED"}
      ],
      "message": "Expense approved"
    }
  ],
  "page": {"next_page_token": null}
}

old_value and new_value are display strings recorded by the activity feed. Their format depends on the changed field and is not normalized to the corresponding API enum. Treat them as descriptive values rather than enum identifiers.

Get an expense file

GET /api/v1/expenses/{expense_id}/files/{file_id} returns metadata and a fresh, short-lived signed download URL for a file attached to the expense. Fetch a new URL when needed instead of storing it. A file attached to another expense returns 404 Not Found.

curl "https://rhoapi.rho.co/api/v1/expenses/0198ba3c-0000-7000-8000-000000000201/files/00000000-0000-0000-0000-0000000000f1" \
  -H "Authorization: Bearer $RHO_API_TOKEN"
{
  "file_id": "00000000-0000-0000-0000-0000000000f1",
  "file_name": "boarding_pass.pdf",
  "download_url": "https://..."
}

Reference

Full endpoint catalogue - paths, parameters, response shapes, and required scopes - lives in the API Reference.