Create a browser session
const url = 'https://api.flotera.com/api/v1/auth/login';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"email":"hello@example.com","password":"example","turnstile_token":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.flotera.com/api/v1/auth/login \ --header 'Content-Type: application/json' \ --data '{ "email": "hello@example.com", "password": "example", "turnstile_token": "example" }'Public endpoint protected by distributed rate limits (IP prefix +
normalized account key). Responses do not distinguish unknown user from
bad password. On success sets the __Host-erm_session cookie
(Secure; HttpOnly; SameSite=Lax; Path=/).
When GET /auth/config reports turnstile.enabled, turnstile_token
is mandatory: the server verifies it against Cloudflare siteverify
after the rate-limit check but before reading the user row or running
Argon2, so a flood of guesses costs the attacker a challenge rather
than costing the server a password hash. Verification is fail
closed — an unreachable or malformed siteverify response rejects the
request (05 §4.3 treats a failing protection store the same way).
A rejected challenge returns 403 with the same body regardless of
whether the account exists, and the token is single-use: clients must
reset the widget before retrying.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Cloudflare Turnstile response token from the widget. Optional in the schema because the challenge is deployment-configurable, but required in practice whenever GET /auth/config reports turnstile.enabled; omitting it then returns 403. Single-use.
Examplegenerated
{ "email": "hello@example.com", "password": "example", "turnstile_token": "example"}Responses
Section titled “Responses”Session created; profile returned.
object
UUID (v7 for new entities; v4 accepted during migration).
UUID (v7 for new entities; v4 accepted during migration).
Display name of the tenant shown in the panel header.
object
IANA timezone, used for quiet hours and daily boundaries in the UI.
object
Electricity price in USD per kWh as a decimal string, never a JSON number: it multiplies into money. The server additionally rejects zero (free electricity is an assertion, and an empty input field is not one) and anything above 100 USD/kWh. Send null to clear the price: the settings object is merged server-side, so a key can be reset but never deleted.
What Vast withholds from rental revenue, as a percentage: the decimal string “20.000000” means 20 %, not 0.2. Zero is a valid rate, not an empty field — Vast removed its host fee in June 2024, so zero is the truthful value there. Must be below 100. Send null to clear.
LEGACY. What Clore withholds from rental revenue, as a single rate for the whole marketplace. Superseded by the per-currency keys below, which the form now writes instead. Still read, and still accepted here, as the fallback for a currency that has no rate of its own: an owner who once stated Clore’s cut must not silently lose that statement.
What Clore withholds when the renter pays in CLORE. On-demand base host fee only — paying in CLORE carries no extra fee. Store the total you actually see, not the breakdown: spot base is 1.25 % and PoH/MFP tiers move it.
What Clore withholds when the renter pays in bitcoin: the base host fee plus the 15 % extra host fee for paying outside CLORE.
What Clore withholds when the renter pays in a dollar stablecoin (USDT/USDC). Also applies to a rental we could only price from the machine’s dollar figure, because that estimate rides the same rail.
What the marketplace adds ON TOP of your price for the renter. This is NOT your expense — it is the conversion factor between the catalogue price and your own. Vast states that “live GPU prices are typically about 25% above what hosts earn”, so a listing at $1.00 corresponds to $0.80 of yours. Without it, advising you to match the market median would set your price at the median and show renters a quarter above it. Convert by dividing by (1 + markup), never by subtracting the percentage. Must not exceed 500.
Same as vast_renter_markup_percent, for Clore, which splits its 10 % base fee 50/50 — the renter’s half sits on top of your price.
Owner’s operating expenses in USD per month for the whole fleet — premises, connectivity, maintenance. Fleet-wide rather than per-server because these costs do not divide by hardware naturally; consumers spread the amount across the month’s billable managed-server-days, the same proration the subscription itself uses, which makes it comparable with per-server electricity. Zero is rejected; send null to clear. Must not exceed 1000000.
Monotonic resource version used for ETag/If-Match.
Example
{ "tenant_settings": { "electricity_usd_per_kwh": "0.083000", "vast_commission_percent": "0.000000", "clore_commission_percent": "20.000000", "clore_commission_clore_percent": "5.000000", "clore_commission_btc_percent": "20.000000", "clore_commission_usd_percent": "20.000000", "vast_renter_markup_percent": "25.000000", "clore_renter_markup_percent": "5.000000", "operational_usd_per_month": "300.000000" }}Headers
Section titled “Headers”__Host-erm_session=<opaque>; Secure; HttpOnly; SameSite=Lax; Path=/
Missing/invalid credentials (code=unauthorized).
RFC 9457 problem document with a stable machine code.
object
Stable machine-readable error code (03 §2.4).
UUID (v7 for new entities; v4 accepted during migration).
object
JSON Pointer to the offending field.
Example
{ "type": "https://ermeon.com/problems/validation", "code": "unauthorized"}Turnstile challenge missing, rejected by Cloudflare, or impossible to verify (fail closed). code=forbidden, detail="captcha verification failed". The body never depends on the submitted credentials; the token is single-use, so the widget must be reset before retrying.
RFC 9457 problem document with a stable machine code.
object
Stable machine-readable error code (03 §2.4).
UUID (v7 for new entities; v4 accepted during migration).
object
JSON Pointer to the offending field.
Example
{ "type": "https://ermeon.com/problems/validation", "code": "unauthorized"}Request failed validation (code=validation_failed), with per-field errors.
RFC 9457 problem document with a stable machine code.
object
Stable machine-readable error code (03 §2.4).
UUID (v7 for new entities; v4 accepted during migration).
object
JSON Pointer to the offending field.
Example
{ "type": "https://ermeon.com/problems/validation", "code": "unauthorized"}Rate limit exceeded (code=rate_limited).
RFC 9457 problem document with a stable machine code.
object
Stable machine-readable error code (03 §2.4).
UUID (v7 for new entities; v4 accepted during migration).
object
JSON Pointer to the offending field.
Example
{ "type": "https://ermeon.com/problems/validation", "code": "unauthorized"}Headers
Section titled “Headers”Seconds to wait before retrying.