The hosted API
Design partners The hosted API is deployed and is open to design partners by invitation. We issue the API host and a machine client with each tenant; there is no self-service sign-up.
It is an HTTP API behind a JWT authorizer. Machine callers use the client-credentials grant with the scope findings.read. The tenant is never taken from the request: it comes from the verified token.
GET /v1/findings
GET /v1/findings/{finding_id}
GET /v1/savings
GET /v1/remediations
- A missing or invalid token returns
401. An unknown path, or a finding that belongs to another tenant, returns404. /v1/findingsreturnscount,total_monthly_costandfindings[], at list prices. It takes no filters yet./v1/savingsreports no verified savings yet, because the hosted service does not read the Cost and Usage Report.- The routes that request, approve and execute a remediation exist under
POST /v1/remediationswith the scopeapprovals.write. Remediation is switched off in the hosted service, so nothing can be executed. - There is no hosted costs route and no hosted OpenAPI document yet.
The self-hosted API
Built, not hosted The sections below describe the API of the self-hosted application. The hosted API does not serve these paths.
OpenAPI document
A self-hosted deployment serves an OpenAPI 3.1 description at /api/v1/openapi.json on its own host. It is not served from this website.
Authentication
| Scheme | For | Configure (self-hosted) |
|---|---|---|
| Bearer token | Machine callers. A static token issued to a named caller | API_TOKENS=name:token,name:token |
| OAuth 2.0 bearer JWT | Machine callers with an identity provider (client credentials) | OIDC_ISSUER, OIDC_AUDIENCE |
| HTTP Basic | People, during the test period | BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD |
curl -H "Authorization: Bearer $SPENDSAW_TOKEN" \
https://<your-host>/api/v1/costs/2026-09
Monthly cost, in FOCUS names
GET /api/v1/costs/{period}
period is a calendar month, YYYY-MM. The response has one row per linked account and service, with column names and meanings from FOCUS 1.0. It is an aggregated subset, not a full FOCUS dataset.
| Field | Meaning |
|---|---|
period | The month requested |
finalized | False while Cost Explorer still marks the month as estimated. The totals can change until it is true |
BilledCost, EffectiveCost | Totals across all rows |
BillingCurrency | Currency of the amounts |
rows[] | One FocusCostRow per account and service |
Each row carries ProviderName, BillingPeriodStart, BillingPeriodEnd (exclusive), SubAccountId, ServiceName, BilledCost, EffectiveCost and BillingCurrency, and may carry ChargePeriodStart, ChargePeriodEnd and SubAccountName.
{
"period": "2026-09",
"finalized": true,
"BilledCost": 412.87,
"EffectiveCost": 412.87,
"BillingCurrency": "USD",
"rows": [
{
"ProviderName": "AWS",
"BillingPeriodStart": "2026-09-01T00:00:00Z",
"BillingPeriodEnd": "2026-10-01T00:00:00Z",
"SubAccountId": "111122223333",
"SubAccountName": "prod-agency",
"ServiceName": "Amazon Elastic Compute Cloud - Compute",
"BilledCost": 187.42,
"EffectiveCost": 187.42,
"BillingCurrency": "USD"
}
]
}
Example values are illustrative. Errors: 400 for a bad period, 401 for a missing or invalid credential, 502 when AWS could not be queried.
Findings
GET /api/v1/findings?waste_type=&account_id=&include_kept=false
Findings from the latest scan, largest saving first, as count, total_monthly_cost and findings[]. Resources protected by a keep tag are left out unless include_kept is true. The fields are described in Findings and cost sources.
Savings
GET /api/v1/savings
| Field | Meaning |
|---|---|
remediations | How many cuts have been made |
expected_monthly_saving | What they were expected to save each month |
verified_saving_to_date | What the Cost and Usage Report has confirmed |
unverified | How many are still waiting for a full billing month |
Errors
{"success": false, "error": "Invalid period '2026-13'; expected YYYY-MM"}
Other routes
These exist in the self-hosted application and are not part of the versioned OpenAPI document:
- The approval flow under
/api/remediations. See Risk tiers and approvals. POST /api/findings/<id>/pull-requestandGET /api/findings/<id>/plan-check. See Terraform pull requests.GET /api/ledgerandPOST /api/ledger/verifyfor the savings ledger and the monthly verification.GET /api/reports/msp/<account_id>.pdffor the white-label monthly report.
Sources: api/spec.py, app.py, security/auth.py, workers/api.py, infra/terraform/modules/api/main.tf.