Changelog
Changes to the API contract (openapi.yaml), newest first. Everything below is additive unless the entry says otherwise; nothing was renumbered.
2026-09-26 — API keys before the first deposit
POST /v1/api-keysworks on anunfundedaccount: sign up, create a key, read the deposit address withGET /v1/deposit-addressesand top up automatically. Orders still need the balance (4001 insufficient_funds).3016 account_unfundedis no longer returned by any route. The code stays reserved.
2026-09-25 — Address resources are public
GET /v1/resources/{address}needs no credentials. An anonymous call runs on the per-IP budget and getsour_active_orders: []; a signed call is verified in full and lists that account’s orders.- The
prices.readscope now guardsPOST /v1/estimate/transferonly.
2026-09-25 — The whole day-part schedule on GET /v1/prices
PriceTablegainsschedule[]:id,label,start_utc_minute,end_utc_minute,factor_bpsand the1henergyprice_sun_per_unit(nullwhen not on sale) for every period of the day, so a price map renders the whole day from one call.periodcomes from the same table. Its ids and bounds changed:drop,off_peak,ramp_9,ramp_11,ramp_12,peak,peak_late. Iterate the schedule; never hardcode its ids or count.
2026-09-19 — Public price table and estimate
GET /v1/pricesandGET /v1/estimateneed no credentials. Anonymous calls run on a per-IP budget; a signed call is verified in full (a bad signature is still401) and runs on the key’s budget.- A signed
GET /v1/estimateis priced for its own account, so a contract customer sees the contract price; an anonymous one gets the retail price.
2026-09-19 — Key format, key limit, dashboard session
- API key format (changes what new key ids look like):
ak_live_+ 24 random bytes (base64url) for the id andsk_live_+ 256 bits for the secret;ak_test_/sk_test_on the Nile host. The prefix names the environment. A key sent to the other environment’s host is refused with the new1012 key_environment_mismatchbefore it is looked up. Keys issued earlier keep their ids and keep working. labelis optional onPOST /v1/api-keys, defaulting toKey N.scopesis unchanged: required, with no default.- Active-key limit.
GET /v1/api-keysreturnslimitandused; past the limit, creation answers the new3017 api_key_limit_reached. ApiKey.last_used_ipalongsidelast_used_at, both written at most once a minute per key.- Dashboard session:
POST,GETandDELETE /v1/sessionandPOST /v1/session/refresh, authenticated by an httpOnly cookie plusX-CSRF-Tokenon writes (1013 session_expired,1014 csrf_token_invalid). A browser credential only; it carries no API-key scopes. PATCH /v1/accountsetsdisplay_name, published onAccountnext tolabelwith the same value.
2026-09-11 — One state machine, one signup model
- Order states:
created → paid → allocating → delegated → confirmed → active → expired | reclaimed, withfailedandrefundedas terminal branches — ten names, the same in every enum, example and webhook. Ordergainspartialanddelivered_amount: partial delivery is a field on aconfirmed/activeorder, not a state.- New webhook event
order.refunded.order.confirmedcarriespartial,delivered_amountandrefunded_amount_sun. - There is no single-order cancel in v1;
3002 order_not_cancellablebelongs toPOST /v1/batches/{id}/cancel. - Signup by address challenge:
POST /v1/accounts/challenge,POST /v1/accounts/challenge/verify(returns a 15-minute bootstrap token),POST /v1/accounts,GET /v1/accounts/deposit-address.Account.statusisunfunded | active | suspended | closed. New codes1010 challenge_invalid,1011 bootstrap_token_expired,3016 account_unfunded. - Scopes: one
area.actionvocabulary (ApiKeyScope).scopesis required on key creation and has no default. - Nile is documented as not identical to mainnet, with the list of differences (Environments).
- Examples use illustrative placeholder prices; live prices come from
GET /v1/prices.