The Accounts Payable API reads the counterparties of the business linked to your API Access Token, and the bills it owes them. These endpoints require the accounts_payable:read scope and are read-only.
The Accounts Payable 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.
Use Accounts Payable to read who you pay and what you owe: the same counterparty and bill records shown in the Rho product. Each counterparty carries the stable id that bills and payments reference as payee, and each bill carries its due date, approval and payment state, line items, and attached invoice file.
GET /api/v1/ap/counterparties returns a paginated list of counterparties. search is a case-insensitive substring match on legal_name and nickname. Filter by one or more status values, sort with sort_by and order, and keep every filter unchanged while paginating.
curl "https://rhoapi.rho.co/api/v1/ap/counterparties?search=acme&status=active&sort_by=legal_name&order=asc&page_size=20" \
-H "Authorization: Bearer $RHO_API_TOKEN"| Parameter | Values | Default | Description |
|---|---|---|---|
search | string | - | Case-insensitive substring match on legal_name and nickname. |
status | incomplete, invited, active, archived | all | Repeatable. Filters by derived display status. |
sort_by | created_at, legal_name | created_at | Sort field. |
order | asc, desc | desc | Sort direction. |
page_size | 1-100 | 20 | Counterparties per page. |
page_token | string | - | Cursor from page.next_page_token. Omit for the first page. |
Follow the cursor as described in Pagination until next_page_token is null. Page tokens belong to the API version and the filters that created them, so restart pagination when migrating from v0 to v1 or when any filter changes.
{
"counterparties": [
{
"id": "3f2b1a44-9c1e-4a8b-bb0e-8b7c2d5e1a90",
"legal_name": "Acme Supplies LLC",
"nickname": "Acme",
"email": "ap@acmesupplies.com",
"phone_number": "+12125550123",
"registration_country_code": "US",
"memo": "Preferred office supplier",
"status": "active",
"is_business": true,
"w9_status": "complete",
"address": {
"street": "123 Market St",
"second_line": "Suite 400",
"city": "New York",
"subdivision": "NY",
"postal_code": "10001",
"country_code": "US"
},
"payment_methods": [
{
"id": "6d083d95-6e5f-4ec3-b125-6eb41cddc63f",
"type": "ach",
"is_default": true,
"description": "Checking ending in 1234",
"bank_name": "Example Bank",
"currency": "USD",
"country_code": "US"
},
{
"id": null,
"type": "single_use_card",
"is_default": false,
"description": "Single-use virtual card",
"currency": "USD",
"country_code": "US"
}
],
"created_at": "2026-07-01T14:32:07Z",
"updated_at": "2026-07-20T09:15:42Z"
}
],
"page": {
"next_page_token": null
}
}Fields that are not on file are omitted from the response rather than returned as empty strings - nickname, email, phone_number, registration_country_code, memo, w9_status, and address are all optional.
GET /api/v1/ap/counterparties/{counterparty_id} returns one counterparty by its stable id, in the same shape as a list entry. A counterparty that does not belong to your business returns 404 Not Found.
curl "https://rhoapi.rho.co/api/v1/ap/counterparties/3f2b1a44-9c1e-4a8b-bb0e-8b7c2d5e1a90" \
-H "Authorization: Bearer $RHO_API_TOKEN"status is derived for display and collapses several underlying states:
| Status | Meaning |
|---|---|
incomplete | A draft missing something needed to pay it - business type, legal name, or a default payment method - or one that failed validation. |
invited | A draft with an unexpired portal invite outstanding, waiting on the counterparty to complete their own details. |
active | Ready to be paid. |
archived | Archived in the Rho product and no longer offered as a payee. |
Archived counterparties are returned like any other, so filter on status=incomplete&status=invited&status=active to exclude them, or status=archived to read only them. They stay readable because bills and payments keep referencing a counterparty after it is archived.
w9_status is absent when there is no W-9 on file. When present:
| Status | Meaning |
|---|---|
complete | A W-9 was collected through Rho. |
complete_externally | A W-9 exists but was collected outside Rho. |
sent_pending | A W-9 request was sent and is awaiting the counterparty. |
payment_methods lists the non-archived payment methods on file, in the order Rho displays them: domestic (ACH or wire), international, check, vendor cards, then a single-use card. At most one is is_default.
| Type | Notes |
|---|---|
ach | Domestic ACH. currency is USD; no address. |
wire | Domestic wire. Carries the beneficiary address when on file. |
check | address is the check shipping address. |
international | bank_name, currency, and country_code come from the beneficiary bank. |
vendor_card | A card issued to this counterparty. |
single_use_card | Available when the counterparty has an email on file. id is null. |
id is null on a single_use_card entry. A single-use card is minted per payment rather than kept on file, so there is no stored record for an id to name. Every other type carries one. The key is always present, so a null id means this type has none rather than that the value went missing. Key off type to tell the entries apart, and expect a null wherever you read payment_methods[].id.
Only non-sensitive payment method metadata is exposed. Bank account and routing numbers are not returned under accounts_payable:read.
is_businessisfalsewhen the counterparty's type is not yet known, which happens onincompletecounterparties. It does not confirm the counterparty is an individual.- Archived payment methods are omitted, and only the first check detail is returned when a counterparty has more than one.
- Counterparties are returned as standalone payees. Related bills are not embedded.
- These endpoints are read-only. Creating, editing, inviting, and archiving counterparties are not available.
GET /api/v1/ap/bills returns a paginated list of bills. Search matches payee name and email, creator name, invoice number, and amount.
curl "https://rhoapi.rho.co/api/v1/ap/bills?status=scheduled&sort_by=due_date&order=asc&page_size=20" \
-H "Authorization: Bearer $RHO_API_TOKEN"| Parameter | Type | Description |
|---|---|---|
search | string | Free-text match across payee name and email, creator name, invoice number, and amount. |
status | BillStatus[] | Repeat the parameter to filter on several statuses. |
due_date_after | date | Earliest due date to include, inclusive. |
due_date_before | date | Latest due date to include, inclusive. |
created_after | date-time | Earliest creation time to include, inclusive. |
created_before | date-time | Latest creation time to include, exclusive. |
sort_by | enum | created_at (default), due_date, or amount. |
order | enum | asc or desc, defaults to desc. |
page_size | integer | 1 to 100, defaults to 20. |
page_token | string | Cursor from page.next_page_token. |
Follow the cursor as described in Pagination until next_page_token is null, and keep filters and sorting unchanged while paginating. Page tokens belong to the API version and the filters that created them, so restart pagination when migrating from v0 to v1.
{
"bills": [
{
"id": "3f2b1a44-9c1e-4a8b-bb0e-8b7c2d5e1a90",
"status": "scheduled",
"amount": {"amount": 120050, "currency": "USD"},
"counterparty": {"id": "7a1c0b22-8d33-4dd9-9d5d-b3171c4c3d71"},
"invoice_number": "BILL-2026-0042",
"invoice_date": "2026-07-24",
"due_date": "2026-08-01",
"lines": [
{
"id": "f6b24a27-4a89-4a89-ae0b-4f4212dd5a15",
"index": 1,
"amount": {"amount": 100000, "currency": "USD"},
"description": "Consulting Services - July",
"created_by_user_id": "a8d32021-5c7d-45f8-8be7-65e9d48db4ec",
"created_at": "2026-07-01T14:32:07Z",
"updated_at": "2026-07-20T09:15:42Z"
}
],
"file_id": "d8593b96-f0f4-4d36-8a5c-7d7d7b9f910a",
"transaction": {"id": "6ef5df3b-50d5-4d7b-9877-dcf8127f1a8d"},
"payment_date": "2026-08-01",
"memo": "Net 30 terms apply.",
"created_at": "2026-07-01T14:32:07Z",
"updated_at": "2026-07-20T09:15:42Z"
}
],
"page": {
"next_page_token": null
}
}Amounts use the currency's smallest unit (cents for USD), so 120050 is $1,200.50. When a bill is itemized, its line amounts sum to amount; lines is empty otherwise. A bill that has no amount set yet - a draft still being filled in, for instance - leaves amount out of the response entirely, so read it as optional rather than defaulting to zero.
counterparty and transaction carry only an identifier. Read the full counterparty record from GET /api/v1/ap/counterparties/{counterparty_id}, and the settled payment from the transactions endpoints.
status folds bill state, approval stage, and payment state into one value.
| Status | Meaning |
|---|---|
draft | Not yet submitted for payment. |
missing_payment_details | Submitted, but blocked until the payment details it needs are on file. |
awaiting_approval | Pending one or more approvals. |
ready_for_payment | Approved and payable, not yet scheduled. |
scheduled | Payment scheduled for a future date. |
pending_payment | Payment in flight, not yet settled. |
paid | Payment settled. |
failed_payment | Payment failed. |
archived | The bill or its payment was archived. |
other | A state that does not map to any value above. |
The table is roughly in lifecycle order, but a bill does not necessarily pass through every state and the order is not guaranteed - don't infer progress from it. Treat other as a catch-all rather than a stable state: new states surface there before they get their own value.
scheduled does not guarantee a payment_date. A bill can be scheduled upstream before a date is attached, so read the two independently.
GET /api/v1/ap/bills/{bill_id} returns one bill by ID. A bill belonging to another business responds 404, the same as one that does not exist.
curl "https://rhoapi.rho.co/api/v1/ap/bills/3f2b1a44-9c1e-4a8b-bb0e-8b7c2d5e1a90" \
-H "Authorization: Bearer $RHO_API_TOKEN"GET /api/v1/ap/bills/{bill_id}/files/{file_id} returns file_id, file_name, and a fresh short-lived signed download_url. file_id must match bill.file_id from list/get when that field is set. Fetch again whenever you need a new signed URL; do not persist the URL.
curl "https://rhoapi.rho.co/api/v1/ap/bills/3f2b1a44-9c1e-4a8b-bb0e-8b7c2d5e1a90/files/d8593b96-f0f4-4d36-8a5c-7d7d7b9f910a" \
-H "Authorization: Bearer $RHO_API_TOKEN"{
"file_id": "d8593b96-f0f4-4d36-8a5c-7d7d7b9f910a",
"file_name": "BILL-2026-0042.pdf",
"download_url": "https://files.example/bill.pdf?signature=..."
}Full endpoint catalogue - paths, parameters, response shapes, and required scopes - lives in the API Reference.