Skip to content

The owner's own cost rates, and what cannot be computed from them

GET
/billing/cost-basis
curl --request GET \
--url https://api.flotera.com/api/v1/billing/cost-basis \
--cookie __Host-erm_session=<__Host-erm_session>

Electricity, marketplace commissions and operating expenses as the owner entered them, plus the derived cost per server-day. These are the CUSTOMER’s costs, not Ermeon’s: the ledger does not know them and the balance does not move because of them. The Ermeon subscription is a separate thing and lives in GET /billing.

Every rate is nullable, and null means “not entered” — never zero. A zero commission would claim the marketplace takes nothing, which overstates what the owner keeps; the write side rejects zero for that reason and asks for null instead.

unknown lists what cannot be computed and why. It is never empty: rental revenue has no source in the system (no producer publishes rental.*), so margin and payback are not derivable here, and market prices are asking prices rather than earnings. The list exists so an automated caller can tell “we don’t know” from “it’s nothing” without guessing from a missing field.

operational.spread shows its own denominator — which month and how many billable server-days the monthly amount was divided by — because a cost per server-day that cannot be checked will be checked anyway.

Cost basis.

Media typeapplication/json
object
currency
required
string
electricity_usd_per_kwh

Null means not entered — never zero.

string | null
operational
required
object
usd_per_month
required
string | null
spread
required

How the monthly amount becomes a per-server-day figure. Null when the amount is set but there is nothing to divide by — that case is also named in unknown, so a set value never vanishes silently.

object
method
required
string
Allowed values: billable_managed_server_days
period
required
string
/^[0-9]{4}-[0-9]{2}$/
server_days
required
integer
>= 1
usd_per_server_day
required
string
marketplaces
required

Two separate kinds of rate per marketplace. commissions is what the platform withholds FROM THE RENTAL — the only deduction that makes revenue smaller — WITH ONE RATE PER DEAL CURRENCY, because Clore withholds 5 % on a CLORE-paid rental and 20 % on a bitcoin-paid one while the same machine accepts both at once. What it costs to withdraw the money is NOT modelled: it depends on the route (network, exchange, bank) chosen at withdrawal time, and a guessed rate would understate earnings systematically. renter_markup_percent is not an expense at all: it converts a catalogue price into your own by dividing by (1 + markup). Mixing the two would advise a listing price a quarter above the market on Vast.

Array<object>
object
marketplace
required
string
Allowed values: vast clore
commissions
required

One entry per deal currency the marketplace settles in. Vast has a single rail (USD) and therefore a single entry — not a formality, the same question with the same answer. percent is the RESOLVED rate: a currency with no rate of its own shows the marketplace-wide fallback, which is exactly what the projection will apply. A form that displayed anything else would let the owner edit one number while the money is computed from another.

Array<object>
object
currency
required

The marketplace’s own spelling, identical to the one stored with the earning fact. No dictionary of ours sits between them — a translation would be the place where the rate and the money drift apart in silence.

string
label
required

Short human name for the field.

string
key
required

Settings key this field PATCHes.

string
percent
string | null
/^[0-9]+(\.[0-9]{1,6})?$/
renter_markup_percent
string | null
/^[0-9]+(\.[0-9]{1,6})?$/
defaults
required

What the form fills a field with until the owner enters their own, each with the source it came from. Served rather than hardcoded in the client so a default can never disagree with what the calculation assumes.

Array<object>
object
key
required
string
value
required
string
source
required

Why this number, in one line, shown under the field.

string
rental_revenue

Rental revenue as ACCRUED by the marketplaces (their commission is already inside their number; withdrawal costs are not). Null while no accrual row exists for the current month — that case is also named in unknown, so absence is a statement, not an omission.

object
source
required

Where the number comes from, so a model never mistakes an accrual for a payout.

string
Allowed values: marketplace_accrual
period
required

Calendar month the window covers.

string
/^[0-9]{4}-[0-9]{2}$/
today_usd
required
string
/^[0-9]+(\.[0-9]{1,6})?$/
mtd_usd
required
string
/^[0-9]+(\.[0-9]{1,6})?$/
marketplaces
required

Per-marketplace breakdown — commissions and rails differ.

Array<object>
object
marketplace
required
string
Allowed values: vast clore
today_usd
required

Non-negative decimal string.

string
/^[0-9]+(\.[0-9]{1,6})?$/
mtd_usd
required

Non-negative decimal string.

string
/^[0-9]+(\.[0-9]{1,6})?$/
unknown
required

What cannot be computed, with the reason and the conclusion to avoid. Empty once every rate is set and at least one accrual row exists for the month.

Array<object>
object
field
required
string
reason
required
string
Allowed values: not_set no_rental_data no_billable_server_days
impact
required
string
Example
{
"currency": "USD",
"electricity_usd_per_kwh": "0.100000",
"operational": {
"usd_per_month": "300.000000",
"spread": {
"method": "billable_managed_server_days",
"period": "2026-07",
"usd_per_server_day": "0.263158"
}
},
"marketplaces": [
{
"marketplace": "vast",
"commissions": [
{
"currency": "CLORE-Blockchain",
"label": "CLORE",
"key": "clore_commission_btc_percent"
}
]
}
],
"defaults": [
{
"key": "clore_commission_percent",
"value": "20.000000"
}
],
"rental_revenue": {
"source": "marketplace_accrual",
"today_usd": "0.082261",
"mtd_usd": "0.196361",
"marketplaces": [
{
"marketplace": "vast"
}
]
},
"unknown": [
{
"field": "rental_revenue",
"reason": "not_set"
}
]
}

Missing/invalid credentials (code=unauthorized).

Media typeapplication/problem+json

RFC 9457 problem document with a stable machine code.

object
type
required
string format: uri
title
required
string
<= 256 characters
status
required
integer
>= 100 <= 599
code
required

Stable machine-readable error code (03 §2.4).

string
Allowed values: unauthorized forbidden csrf_rejected not_found conflict idempotency_conflict validation_failed rate_limited payload_too_large unsupported_agent_version temporarily_unavailable offline_queue_full online_queue_full plan_required server_limit_reached feature_not_entitled account_in_grace account_frozen payment_pending payment_expired payment_amount_mismatch change_already_pending change_already_applied change_effective reserve_not_covered direction_changed
detail
string
<= 2048 characters
instance
string
<= 512 characters
request_id

UUID (v7 for new entities; v4 accepted during migration).

string format: uuid
errors
Array<object>
<= 100 items
object
path
required

JSON Pointer to the offending field.

string
<= 512 characters
code
required
string
<= 64 characters
Example
{
"type": "https://ermeon.com/problems/validation",
"code": "unauthorized"
}

Authenticated but not permitted. code=forbidden — role/scope mismatch; code=feature_not_entitled — the plan does not include the capability; code=account_frozen — the account is frozen and this operation is declared x-ermeon-frozen: deny. The three are deliberately distinct: only the last one is fixed by a top-up (13 §5.6).

Media typeapplication/problem+json

RFC 9457 problem document with a stable machine code.

object
type
required
string format: uri
title
required
string
<= 256 characters
status
required
integer
>= 100 <= 599
code
required

Stable machine-readable error code (03 §2.4).

string
Allowed values: unauthorized forbidden csrf_rejected not_found conflict idempotency_conflict validation_failed rate_limited payload_too_large unsupported_agent_version temporarily_unavailable offline_queue_full online_queue_full plan_required server_limit_reached feature_not_entitled account_in_grace account_frozen payment_pending payment_expired payment_amount_mismatch change_already_pending change_already_applied change_effective reserve_not_covered direction_changed
detail
string
<= 2048 characters
instance
string
<= 512 characters
request_id

UUID (v7 for new entities; v4 accepted during migration).

string format: uuid
errors
Array<object>
<= 100 items
object
path
required

JSON Pointer to the offending field.

string
<= 512 characters
code
required
string
<= 64 characters
Example
{
"type": "https://ermeon.com/problems/validation",
"code": "unauthorized"
}