The five statements an owner or an accountant asks for.
5 endpoints, each with the fields it takes and the fields it gives back.
GET/reports/profit-and-loss
Returns the profit and loss statement for one business over a date range.
- Leave
entityIdout only when this key can reach exactly one business. When it can reach several, a call that omits it is refused; callGET /businessesfor the ids. prioris the calendar month before the monthperiodStartfalls in, whatever range you ask for.- An account with no activity in either period is left out.
- Figures follow the business's own accounting basis.
basisin the response says which one.
Needs the reports:read permission. A Read only key has it.
Query parameters
- a uuid
- a date like 2026-09-20
- a date like 2026-09-20
| Field | Type | Required | Details |
|---|---|---|---|
| entityId | string | Optional |
|
| periodStart | string | Required |
|
| periodEnd | string | Required |
|
curl "https://www.numm.io/api/v1/reports/profit-and-loss?entityId=0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f1002&periodStart=2026-01-01&periodEnd=2026-01-31" \
-H "Authorization: Bearer nmo_live_REPLACE_WITH_YOUR_KEY"{
"entity": "Acme Co",
"basis": "accrual",
"period": {
"start": "2026-01-01",
"end": "2026-01-31"
},
"rows": [
{
"account": "Consulting Revenue",
"type": "revenue",
"depth": 1,
"isTotal": false,
"amount": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
},
"prior": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
},
{
"account": "Total revenue",
"type": "total",
"depth": 0,
"isTotal": true,
"amount": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
},
"prior": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
}
],
"totals": {
"revenue": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
},
"expenses": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"netIncome": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
}
},
"prior": {
"revenue": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"expenses": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"netIncome": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
}
}Response fields
- one of: cash, accrual
- a date like 2026-09-20
- a date like 2026-09-20
- one of: revenue, expense, total
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
| Field | Type | Present | Details |
|---|---|---|---|
| entity | string | Always | |
| basis | string | Always |
|
| period | object | Always | |
| period.start | string | Always |
|
| period.end | string | Always |
|
| rows | array of object | Always | |
| rows[].account | string | Always | |
| rows[].type | string | Always |
|
| rows[].depth | integer | Always | |
| rows[].isTotal | boolean | Always | |
| rows[].amount | money | Always |
|
| rows[].prior | money | Always |
|
| totals | object | Always | |
| totals.revenue | money | Always |
|
| totals.expenses | money | Always |
|
| totals.netIncome | money | Always |
|
| prior | object | Always | |
| prior.revenue | money | Always |
|
| prior.expenses | money | Always |
|
| prior.netIncome | money | Always |
|
Errors
| Status | Code | Meaning |
|---|---|---|
| 403 | entity_not_allowed | Your key is limited to certain businesses, and this is not one of them. |
| 404 | not_found | There is no such record, or none your key can reach. |
Every endpoint can also return:
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_key | Your key is missing, malformed, revoked, or expired. |
| 403 | insufficient_scope | Your key does not carry the scope this endpoint needs. |
| 429 | rate_limited | You've made too many requests for this key. The retry-after header says how long to wait. |
| 422 | validation_failed | Something in the path, the query, or the body did not pass validation. |
| 500 | internal_error | Something went wrong on our side. Try again. |
GET/reports/balance-sheet
Returns the balance sheet for one business as of a date.
- Leave
entityIdout only when this key can reach exactly one business. When it can reach several, a call that omits it is refused; callGET /businessesfor the ids. asOfis required. The report is the position as of that date.- Figures follow the business's own accounting basis.
basisin the response says which one.
Needs the reports:read permission. A Read only key has it.
Query parameters
- a uuid
- a date like 2026-09-20
| Field | Type | Required | Details |
|---|---|---|---|
| entityId | string | Optional |
|
| asOf | string | Required |
|
curl "https://www.numm.io/api/v1/reports/balance-sheet?entityId=0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f1002&asOf=2026-01-31" \
-H "Authorization: Bearer nmo_live_REPLACE_WITH_YOUR_KEY"{
"entity": "Acme Co",
"basis": "accrual",
"asOf": "2026-01-31",
"rows": [
{
"account": "Total assets",
"type": "total",
"depth": 0,
"isTotal": true,
"amount": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
}
],
"totals": {
"assets": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"liabilities": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"equity": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
}
}Response fields
- one of: cash, accrual
- a date like 2026-09-20
- one of: asset, liability, equity, total
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
| Field | Type | Present | Details |
|---|---|---|---|
| entity | string | Always | |
| basis | string | Always |
|
| asOf | string | Always |
|
| rows | array of object | Always | |
| rows[].account | string | Always | |
| rows[].type | string | Always |
|
| rows[].depth | integer | Always | |
| rows[].isTotal | boolean | Always | |
| rows[].amount | money | Always |
|
| totals | object | Always | |
| totals.assets | money | Always |
|
| totals.liabilities | money | Always |
|
| totals.equity | money | Always |
|
Errors
| Status | Code | Meaning |
|---|---|---|
| 403 | entity_not_allowed | Your key is limited to certain businesses, and this is not one of them. |
| 404 | not_found | There is no such record, or none your key can reach. |
Every endpoint can also return:
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_key | Your key is missing, malformed, revoked, or expired. |
| 403 | insufficient_scope | Your key does not carry the scope this endpoint needs. |
| 429 | rate_limited | You've made too many requests for this key. The retry-after header says how long to wait. |
| 422 | validation_failed | Something in the path, the query, or the body did not pass validation. |
| 500 | internal_error | Something went wrong on our side. Try again. |
GET/reports/trial-balance
Lists every account's balance as of a date; accountants ask for this.
- Leave
entityIdout only when this key can reach exactly one business. When it can reach several, a call that omits it is refused; callGET /businessesfor the ids. asOfis required. The report is the position as of that date.- Figures follow the business's own accounting basis.
basisin the response says which one.
Needs the reports:read permission. A Read only key has it.
Query parameters
- a uuid
- a date like 2026-09-20
| Field | Type | Required | Details |
|---|---|---|---|
| entityId | string | Optional |
|
| asOf | string | Required |
|
curl "https://www.numm.io/api/v1/reports/trial-balance?entityId=0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f1002&asOf=2026-01-31" \
-H "Authorization: Bearer nmo_live_REPLACE_WITH_YOUR_KEY"{
"entity": "Acme Co",
"basis": "accrual",
"asOf": "2026-01-31",
"rows": [
{
"account": "Office Expense",
"debit": {
"cents": "5000",
"formatted": "$50.00",
"currency": "USD"
},
"credit": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
}
],
"totals": {
"debit": {
"cents": "5000",
"formatted": "$50.00",
"currency": "USD"
},
"credit": {
"cents": "5000",
"formatted": "$50.00",
"currency": "USD"
}
}
}Response fields
- one of: cash, accrual
- a date like 2026-09-20
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
| Field | Type | Present | Details |
|---|---|---|---|
| entity | string | Always | |
| basis | string | Always |
|
| asOf | string | Always |
|
| rows | array of object | Always | |
| rows[].account | string | Always | |
| rows[].debit | money | Always |
|
| rows[].credit | money | Always |
|
| totals | object | Always | |
| totals.debit | money | Always |
|
| totals.credit | money | Always |
|
Errors
| Status | Code | Meaning |
|---|---|---|
| 403 | entity_not_allowed | Your key is limited to certain businesses, and this is not one of them. |
| 404 | not_found | There is no such record, or none your key can reach. |
Every endpoint can also return:
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_key | Your key is missing, malformed, revoked, or expired. |
| 403 | insufficient_scope | Your key does not carry the scope this endpoint needs. |
| 429 | rate_limited | You've made too many requests for this key. The retry-after header says how long to wait. |
| 422 | validation_failed | Something in the path, the query, or the body did not pass validation. |
| 500 | internal_error | Something went wrong on our side. Try again. |
GET/reports/ar-aging
Returns unpaid customer invoices bucketed by how overdue they are.
- Leave
entityIdout only when this key can reach exactly one business. When it can reach several, a call that omits it is refused; callGET /businessesfor the ids. asOfdefaults to the current date in UTC.bucketsandgrandTotalcover every unpaid invoice.itemslists only the 25 largest by balance.currentmeans not yet overdue, anddaysOverdueis negative for something not yet due. The other buckets are 1 to 30, 31 to 60, 61 to 90, and more than 90 days past due.
Needs the reports:read permission. A Read only key has it.
Query parameters
- a uuid
- a date like 2026-09-20
| Field | Type | Required | Details |
|---|---|---|---|
| entityId | string | Optional |
|
| asOf | string | Optional |
|
curl "https://www.numm.io/api/v1/reports/ar-aging?entityId=0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f1002" \
-H "Authorization: Bearer nmo_live_REPLACE_WITH_YOUR_KEY"{
"buckets": {
"current": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
},
"d1_30": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"d31_60": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"d61_90": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"d90plus": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
},
"grandTotal": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
},
"items": [
{
"id": "0190a1b2-c3d4-7e5f-9a9b-0c1d2e3f1007",
"customerOrVendor": "Acme Co",
"number": 1,
"dueDate": "2026-06-15",
"daysOverdue": -10,
"balance": {
"cents": "10000",
"formatted": "$100.00",
"currency": "USD"
}
}
]
}Response fields
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- a uuid
- a date like 2026-09-20
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
| Field | Type | Present | Details |
|---|---|---|---|
| buckets | object | Always | |
| buckets.current | money | Always |
|
| buckets.d1_30 | money | Always |
|
| buckets.d31_60 | money | Always |
|
| buckets.d61_90 | money | Always |
|
| buckets.d90plus | money | Always |
|
| grandTotal | money | Always |
|
| items | array of object | Always | |
| items[].id | string | Always |
|
| items[].customerOrVendor | string | Always | |
| items[].dueDate | string | Always |
|
| items[].daysOverdue | integer | Always | |
| items[].balance | money | Always |
|
| items[].number | integer | Always |
Errors
| Status | Code | Meaning |
|---|---|---|
| 403 | entity_not_allowed | Your key is limited to certain businesses, and this is not one of them. |
| 404 | not_found | There is no such record, or none your key can reach. |
Every endpoint can also return:
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_key | Your key is missing, malformed, revoked, or expired. |
| 403 | insufficient_scope | Your key does not carry the scope this endpoint needs. |
| 429 | rate_limited | You've made too many requests for this key. The retry-after header says how long to wait. |
| 422 | validation_failed | Something in the path, the query, or the body did not pass validation. |
| 500 | internal_error | Something went wrong on our side. Try again. |
GET/reports/ap-aging
Returns what one business owes, bucketed by how long it has been outstanding.
- Leave
entityIdout only when this key can reach exactly one business. When it can reach several, a call that omits it is refused; callGET /businessesfor the ids. - This counts reimbursements that have been approved and not yet paid, and accepted expenses that are not reimbursable and did not come from a bank feed.
asOfdefaults to the current date in UTC.bucketsandgrandTotalcover everything owed.itemslists only the 25 largest by balance.daysOverduehere counts days since the expense's date or the reimbursement's approval, not days past a due date, anddueDatecarries that same date.currentcovers anything dated on or afterasOf. The other buckets are 1 to 30, 31 to 60, 61 to 90, and more than 90 days outstanding.
Needs the reports:read permission. A Read only key has it.
Query parameters
- a uuid
- a date like 2026-09-20
| Field | Type | Required | Details |
|---|---|---|---|
| entityId | string | Optional |
|
| asOf | string | Optional |
|
curl "https://www.numm.io/api/v1/reports/ap-aging?entityId=0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f1002" \
-H "Authorization: Bearer nmo_live_REPLACE_WITH_YOUR_KEY"{
"buckets": {
"current": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"d1_30": {
"cents": "5000",
"formatted": "$50.00",
"currency": "USD"
},
"d31_60": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"d61_90": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
},
"d90plus": {
"cents": "0",
"formatted": "$0.00",
"currency": "USD"
}
},
"grandTotal": {
"cents": "5000",
"formatted": "$50.00",
"currency": "USD"
},
"items": [
{
"id": "0190a1b2-c3d4-7e5f-9a9b-0c1d2e3f1008",
"customerOrVendor": "Office Depot",
"number": null,
"dueDate": "2026-02-05",
"daysOverdue": 5,
"balance": {
"cents": "5000",
"formatted": "$50.00",
"currency": "USD"
}
}
]
}Response fields
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- a uuid
- a date like 2026-09-20
- an object:
cents(whole cents as a digit string, in the currency beside it),formatted(a display string, not something to parse),currency(a three-letter code)
- always null
| Field | Type | Present | Details |
|---|---|---|---|
| buckets | object | Always | |
| buckets.current | money | Always |
|
| buckets.d1_30 | money | Always |
|
| buckets.d31_60 | money | Always |
|
| buckets.d61_90 | money | Always |
|
| buckets.d90plus | money | Always |
|
| grandTotal | money | Always |
|
| items | array of object | Always | |
| items[].id | string | Always |
|
| items[].customerOrVendor | string | Always | |
| items[].dueDate | string | Always |
|
| items[].daysOverdue | integer | Always | |
| items[].balance | money | Always |
|
| items[].number | null | Always |
|
Errors
| Status | Code | Meaning |
|---|---|---|
| 403 | entity_not_allowed | Your key is limited to certain businesses, and this is not one of them. |
| 404 | not_found | There is no such record, or none your key can reach. |
Every endpoint can also return:
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_key | Your key is missing, malformed, revoked, or expired. |
| 403 | insufficient_scope | Your key does not carry the scope this endpoint needs. |
| 429 | rate_limited | You've made too many requests for this key. The retry-after header says how long to wait. |
| 422 | validation_failed | Something in the path, the query, or the body did not pass validation. |
| 500 | internal_error | Something went wrong on our side. Try again. |