Changelog
Notable Releases
October 2026
- Provider cache hit rate. Each provider row of
GET /api/analytics/performance/{model}addscache_hit_rate_pct: the share of that provider's input tokens that were prompt-cache reads over the trailing 24 hours, to one decimal. It isnulluntil the whole window is measured, and a0is a real measurement. The Current offers tables on the Markets page and the model page add a sortable Cache column with the same figure, one value per provider, with offers that have no figure sorting last; image, video and audio models have no Cache column. See Provider Performance. - Secret masking (beta). An optional extra layer of protection for text prompts: API keys, private keys, seed phrases, passwords and tokens in a request are replaced with placeholders before it is routed, and every value is put back in the response, wherever the model writes it - passwords, seed phrases, links and markup included - except in the model's reasoning, which comes back as written. It is off by default and opt-in at every layer: send
X-SI-Mask-Secrets: on, or setmask_secrets: trueon a key (PUT /v1/buyer/keys/{id}/preferences,POST /v1/keys,PATCH /v1/keys/{id}) or on the account (PUT /v1/buyer/preferences), or use the switches on Buy › Routing and on key create and edit. A key cannot opt out of an account that masks. A write that turns it on returnssecret_masking_effective_at(about 31 seconds later); the preference responses addsecret_masking_available. Masked responses carryx-si-secret-masking,x-si-secrets-maskedand, when placeholders were left in place,x-si-secrets-withheld(: si-secrets-withheld <n>on a stream). It covers chat completions, completions, Responses, the Anthropic skin and Decisions; a masked key or account still works on embeddings and media, which are sent as they are, without masking (x-si-secret-masking: not_applied). While masking is running, a request that asks for it and cannot get it is refused, never sent unmasked, with one of nine codes; while it is paused on the service, such a request is sent without masking. Restoring every value trusts the seller's model with where it goes - into commands, links and images too - so read what that means first: Secret masking. - Preference writes merge instead of replacing (behaviour change).
PUT /v1/buyer/preferencesandPUT /v1/buyer/keys/{id}/preferencesnow mergepreferencesinto what is stored: a field you leave out is unchanged andnullclears that one field. Before, the body replaced every stored preference, so{ "preferences": {} }cleared them all; it now changes nothing. OnPATCH /v1/keys/{id},provider_trust: "default"clears only the trust setting. A write that loses a race three times answers409 concurrent_update, which is safe to retry. See Buyer Endpoints. - OpenAI's Decisions API on Surplus.
POST /v1/decisionsnow speaks OpenAI's public Decisions API: point the OpenAI SDK at Surplus and callclient.decisions.createwithgpt-6-luna-input(text or user messages with base64 images) and an ordered array ofpredicate,choiceandscorequestions, answered in order with per-question refusals. The JEV format (state+ a map of questions) keeps working unchanged; a JEV request sent to an OpenAI-format model is translated only when nothing is lost (else400 decisions_mapping_lossy). Billed on input tokens only, at $0.10 per 1M.Idempotency-Keyis no longer used on/v1/decisions(as on the other inference endpoints), and the endpoint documents its200status. See Decisions. - The signed-in app moved to
/app(URL change). Every page that used to live at/buy,/sell,/settings,/finops,/keys,/invite,/imagegenand/paymentsis now under/app-/buy/keysis/app/buy/keys,/finops/requestsis/app/activity/requests,/finops/savingsis/app/buy/savings,/finops/webhooksis/app/settings/webhooks, and/settings/budgetsis/app/buy/budgets. Old links keep working: every one of them redirects, so bookmarks, emails and integrations do not break. Three things worth knowing: the redirect is temporary (307) while the change settles, so do not cache it;/miniappnow goes to the home page, because the Farcaster Mini App is retired; and/imagegengoes to the dashboard home rather than to an image page, because there is no image surface in the new app yet. The API is unaffected -/api/*and/x402/*are unchanged, and no request path, header or payload moves. - Notification links point at the page that fixes the problem. A low-balance or auto-top-up email now opens Funding directly, a dead seller key or unhealthy offer opens your Offers table rather than Earnings, and "Manage alerts" always opens Settings › Notifications. Stripe now returns you to Funding by default instead of the old buy page.
September 2026
- Privacy: identities no longer cross sides (breaking). The public order book (
GET /api/markets/:model) dropsoffers[].seller, a seller's wallet; seller earnings and/v1/seller/earnings-historydroprecent_sales[].buyer, a buyer's wallet; the usage leaderboard's entries drophandleandlabel(a rank is the entry's position, and each entry carries onlyspend_micro); and Explorergroup_by=provider/filter_providernow use the provider route, never a seller's wallet. Buyers never see a seller's identity and sellers never see a buyer's. - Buyer overview and spend analytics. New
GET /v1/buyer/overview?range=7d|30d|90dreturns spend, savings, the 7-day pace (avg_daily_spend_micro; an account younger than a week is paced over its complete days so far), daily figures and top models in one call, and sayscoverage: falserather than showing zeros when the figures aren't available./v1/analytics/spend/*and/v1/analytics/savings/summaryacceptworkspace_id=<id>orall(all needs audit access on a team org) and addcache_read_tokens,cache_write_tokens,cache_hit_requests,fiat_spend_micro,usdc_spend_micro,prompt_cache_savings_micro,prompt_cache_hit_pct,cache_routed_pct,distinct_models,distinct_keys,distinct_providersandcache_routing_mode; top model and key lists go up to 50, and provider groups are provider routes.cache_hit_pctstays as a deprecated alias of the cache-routing figure. - Prompt-cache savings, and one rule for "saved".
/v1/buyer/savingsaddscache_saved_usdcandcache_saved_since: what prompt caching saved you, measured from the date shown. Everywhere a saving is shown it is signed per request and floored at zero on the total, so a request that cost more than the reference is counted honestly. - Explorer, request log and sales history. The Explorer adds comma-list filters, a
daygrouping,order_by/order_dir, settlement, funding and cache filters, the cache and funding-split measures andprompt_cache_hit_pct; sellers can query their sales withside=seller.GET /v1/requestsacceptsfrom/to, comma-list filters,statuses,member_id,funding,endpoint,cacheandlatency_*, and each row carries its settlement status; the request detail adds cache tokens, the fiat/USDC split and whether the seller is trusted./v1/seller/earnings-historyacceptsfrom,to,model,offer_id(up to 10 offers, merged newest first) andsettlement(pending,owed,paid, orconfirmedfor every settled sale; the seller Explorer acceptsconfirmedtoo). A filtered read that stops at its read budget says so withtruncated, and filtered log reads have their own rate limit (60 per minute; an audit read filtered byworkspace_idcounts as filtered). - Audit. New
GET /v1/audit/{entryId}; entries carry a hashedsession_ref; the list filters onoutcome(now includingerror),workspace_idandsession_ref; a day-onlytocovers the whole day; and denied actions are recorded. - Payments and budgets.
GET /v1/payments/balanceaddsprefunded_usdc_enabled,withdrawals_pausedandpending_deposits[](amount, observed time, live confirmations, status and sending address), anddeposit_min_confirmationswhenever a deposit is pending.GET /v1/payments/auto-topupadds the card on file (card_brand,card_last4,card_exp_month,card_exp_year).POST /v1/payments/topupandPOST /v1/payments/billing-portalacceptreturn_to(legacy,dashboardor a path under/app; anything else is400 invalid_return_to). NewGET /v1/payments/history, your funding history with cursor paging, is rolling out per environment. NewGET /v1/budgets/spendreturns every visible budget's current spend in one call (a member confined to one workspace also sees the organization budget and their own member budget), and budget spend addsscope_type,scope_id,statusandhard_stop. A paused budget now keeps counting spend: it never blocks a request or sends an alert while paused, but its spend is current when you resume it. - Seller earnings go deeper.
/v1/seller/earningsaddscomputed_at,range=today|mtd,share.prior(the previous equal period), a token-weightedavg_discount_given_pct,cache_savings_usdc, cache and input/output tokens,model_count,offer_count,top_models_by_earnings, andby_offer/by_providerfor today, 7 days, 30 days and month to date. Recent sales adddiscount_pct,provider(omitted when it wasn't recorded),sold_atandconfirmed_at. - Seller offers and payouts. Offers add
discount_pct,provider,route_kind(direct, aggregator or cloud),credential_kind,credential_prefix, and the last error (last_error_category,last_status_code,last_error_at) withlast_probe_atandunhealthy_since; health-test lines addstatus_codeanderror_category. NewPOST /v1/seller/offers/bulk-updateruns one action over a selection (offer_idsand/orprovider):cancel(a selection is required),reset_health(a media offer still awaiting or failing its render check is not re-admitted), andset_discountbulk repricing, rolling out behind a flag, which reprices discount-priced text and media offers at exactly the prices setting that discount on one offer gives (offers you priced per token, and text models without a current reference price, are counted infailed_count). Aproviderselection matches offers the way your provider cards name them; if the provider list can't be loaded the operation changes nothing, retries, and otherwise endsfailedwitherror_type: ProviderIdentitiesUnavailable. Pausing, resuming, or editing an offer's daily cap, payout address or key no longer changes how a discount-priced text offer is labelled (pricing_reference), so its displayed discount stays put; only a price edit marks it as priced per token.POST /v1/seller/offers/bulk-cancelstill cancels every active offer and now refuses a body namingoffer_idsorprovider(400 selector_not_supported). Every bulk operation, Cancel all included, fillstotalonce it starts running.GET /v1/seller/payoutsaddsmode, the payout mode that actually applies to you, and history rows addtype,methodandpaid_at; the withdraw response'smodenow reports that same effective mode (batchedwherever batched payouts are on). - Where your offers stand. Seller offers (
GET /v1/seller/offersand/v1/seller/offers/{id}) addbest_discount_pct,gap_pct,is_best,outbid,requests_24h,requests_7d,earned_7d_usdcandlast_sale_at, and the list addssummary, counted over all your offers whatever the page: active, healthy, failing, auth-error and at-best offers, outbid offers, models and providers live, the last 24 hours' requests and earnings, per-provider and per-model rows, andchecked_at. Anything not yet known isnull. - Markets.
GET /api/marketsrows and the order book'sdepthaddbest_live_discount_pct,median_live_discount_pctandbest_trusted_offer_discount_pct, measured against each model's reference price (omitted ornullwhen there is none, never 0). The order book addsrealized_7d(the average discount buyers actually got over 7 days, with the week before),discount_trend_7d(the best realized discount per 12 hours),recent_fills[].routeandrecent_fills_as_of./api/marketplaceaddsrequests_24h,seller_earned_24h_micro,sellers_live,trusted_sellers_live,providers_live, seller best-discount histograms andcomputed_at. Provider performance addsp95_latency_ms, and public usage addsdiscount_7d,discount_histogram_7dand uncached input tokens (input_tokensis gross). - Keys, routes and your account. API keys'
last_used_atis kept current (seller and admin keys to within five minutes), and your own provider routes (/v1/buyer/providers) addlast_used_atanduses_this_week(a route added from now on is counted from creation and reads 0 until first used). Session org entries addrole, and the session andPOST /v1/auth/active-orgaddplatform_admin. Notification rules addstateandlast_triggered_at. NewGET /v1/orgs/:orgId/invitesandDELETE /v1/orgs/:orgId/invites/:inviteIdlist and revoke pending invites. - Keys mean your API keys. Key counts and key breakdowns in spend analytics (
distinct_keys,dim=key), the Explorer's key grouping, filter and CSV export, and the request log'skey_id(rows,GET /v1/requests/facetsand the filter) now count and list only your account's own API keys. Requests from a signed-in session or paid with x402/MPP still count in every total but show no key (key_id: null); filtering by anything other than one of your keys returns 400 (invalid_key_idon the request log,invalid_explorer_queryon the Explorer). Therequest.failedwebhook'skey_idis the API key the request used, ornull. - Providers onboarded through configuration show as themselves. A provider added without a code release (for example FusionCode or BytePlus) now appears under its own provider id, never
unknown, on seller earnings (by_provider, recent sales), seller offers (provider,summary.providers), the marketplace'sproviders_live, spend analytics provider groups, the Explorer's provider route, and each market's per-provider breakdown on/api/marketsand/api/marketplace.GET /api/providersentries addprovider_id, the id every one of those surfaces uses for the provider (for examplegooglefor both Google entries), so a client can turn any of them into a displayname. - Notifications. The low-balance alert rule reads "Notify me when my balance drops below the amount I set.", and notification emails meet WCAG AA contrast and fit a phone's width.
- Browser errors and CORS. New
POST /v1/client-errorsandPOST /v1/client-errors/csptake scrubbed, rate-limited error and CSP reports from our web apps. Credentialed cross-origin calls are now allowed only from the Surplus dashboard's own origins; any other origin getsAccess-Control-Allow-Origin: *without credentials. API-key calls are unaffected. - The four CSV exports are deprecated. They now send
DeprecationandSunsetheaders and stop working on 2027-06-30; the dashboard's Activity pages and the JSON endpoints above replace them. - Restricted keys stay restricted. An API key with a scope, trusted-only provider setting, budget, or expiry, and every team-workspace key, can no longer create, rename, configure, or revoke keys, or change account routing preferences or BYOK providers, on
/v1/buyer/*and/v1/seller/keys. It gets403 restricted_key, can still read keys, and can revoke itself. Before this, such a key could mint an unrestricted sibling. Unrestricted keys and dashboard sessions work as before. See Buyer Endpoints. - Async media stops failing you quietly. A video or music job whose only sellers are listed against a host that cannot run that endpoint now returns
404 no_sellers_for_modelup front, instead of being accepted, charged and failed later; those offers are being removed from the marketplace. Both async endpoints now document their full error table, including which failures are retryable. See Video Generations and Music Generations. - One prepaid balance, reported the same everywhere.
GET /v1/buyer/mereportscredit_balance_usdcas your whole spendable prepaid balance - fiat credit plus deposited USDC - the same figure/v1/payments/balancealready returned. Deposited USDC can also fund video and music jobs, which previously saw only fiat credit and could refuse a funded buyer. - OpenAI-backed image edits work. Edits routed to OpenAI were rejected by the provider because we sent the wrong field shape.
POST /v1/images/editsnow works on those models; nothing about the request you send changed. See Image Generations → Image-to-Image. - Unknown image sizes no longer error internally. An unrecognized
sizeused to be able to fail the price estimate outright; it is now handled cleanly. - Latency and uptime on the offers table: expanding a model on the Markets page now shows each offer's provider latency (median time to first token) and uptime over the trailing 24 hours, both sortable.
-means not enough data. See Provider Performance. - Model latency, uptime, cache hit rate and throughput on the catalog list: every row of
GET /api/marketsnow carriesttft_p50_ms, the model's median time to first token over the trailing 24 hours across all of its providers (withttft_samplesbeside it),uptime_pctover the same window,cache_hit_rate_pct, the share of input tokens served from the prompt cache, andtps_p50, the median output tokens per second while streaming. The per-provider performance endpoint gainstps_p50andtps_samplestoo.nullmeans not enough data to measure, never zero. See Market Data. - Per-request cost on your API key: buyer API keys can read the exact settled cost of their own requests, and
/v1/chat/completionsreturns a settlement request id so a streamed request's final cost is retrievable once the stream ends. - Public provider performance and benchmarked discounts: each model's marketplace page shows measured per-provider performance, and every displayed discount is benchmarked against live upstream list prices. See Provider Performance.
- Routing controls: pin a request to a chosen set of providers, and set a per-key minimum-discount floor (replacing the older price ceiling) so a key only routes to offers at or above the saving you require. See Routing Controls and Minimum-Discount Routing.
- Cache and reasoning in usage analytics: usage now breaks out cached input tokens and reasoning tokens per request, so token mix and cost are visible at a glance. See Reasoning Controls.
- Streaming keep-alive: long generations stay connected while a provider is briefly silent, so a slow but valid response no longer times out mid-stream.
- Marketplace filters: the marketplace has buyer-facing filters and a dedicated mobile filter view for narrowing the model list without leaving the page.
August 2026
- Reasoning delivered and billed correctly, end to end: reasoning content is relayed across both the OpenAI and Anthropic wires and in both directions, reasoning tokens are persisted for billing and analytics, and Gemini reasoning survives multi-turn tool calls. See Reasoning Controls.
- No charge for undelivered output: in-band provider stream faults are detected mid-stream, so a request that delivers zero output tokens is not billed.
- Organizations: roles and permissions are explained in-product, with documentation for members, workspaces, wallets, API keys, and organization analytics. See Organizations.
- API-first key management: buyer and seller keys can be created, rotated, and managed entirely over the API. See Buyer Endpoints and Seller Endpoints.
- One active offer per credential, base URL, and model: duplicate seller offers are now rejected across every workspace, so the same credential cannot be listed against the same model twice.
July 2026
- Image-to-image editing: new
POST /v1/images/editsendpoint: edit an existing image with a text prompt using an edit-capable model (e.g.nano-banana-2-edit,qwen-image-2-edit).POST /v1/images/generationsruns the same edit flow when the body carries a source image (image_url,input_images, or adata:URI). Masked/inpaint edits are not supported yet. See Image Generations → Image-to-Image. - Image-to-video:
POST /v1/video/generationsaccepts a start frame (image_url), a transitionend_image_url, and multi-imageinput_images(rolesstart/end/reference). Image-to-video-only models require a source image. See Video Generations → Image-to-Video. - Multi-image inputs: both surfaces accept up to 8 source images via
input_images(strings or{ url, role }objects). On images, the first entry is the base and the rest are reference/edit layers; on video, untagged entries are positional ([start]or[start, end]) and further images take explicit roles. Sources arehttps://URLs ordata:URIs; URLs are passed to the provider as-is and never fetched by the marketplace.
June 2026
- Multi-provider pinning: the
providerhint on/v1/chat/completionsnow accepts an array (an allow-list), e.g."provider": ["zai", "openrouter"], routing to the cheapest healthy offer among the listed providers. A single string still works unchanged. Unrecognized providers are dropped; if every pinned provider is unrecognized the request returns400 unsupported_provider(a supported provider with no offer for the model still returns404 no_sellers_for_model). See Chat Completions → Provider Pinning. - API moved to AWS: the API base host is now
https://api.surplusintelligence.ai. Inference endpoints live under/v1/*(e.g./v1/chat/completions,/v1/models,/v1/prices), markets under/api/markets, and account endpoints under/v1/buyer/*and/v1/seller/*. Update yourbase_urltohttps://api.surplusintelligence.ai/v1. The legacy dedicated fallback-key endpoint was retired; register a catch-all provider withmodel: nullviaPOST /v1/buyer/providersinstead.
April 2026
- Bankr LLM Gateway: 32 models at 0.05x cost, self-sustaining auto top-up loop (PRs #55-57)
- External wallet auth fix: Rainbow, WalletConnect now create inference sessions (PR #60)
- GLM 5.1 added to model registry (PR #59)
- $500/day giveaway launched
March-April 2026
- Model naming unification: single source of truth introduced with 58 models / 8 providers at the time (PRs #19-26). Current live counts are higher; use
/v1/modelsand/api/marketsfor current numbers. - Tool format normalization: Cursor full compatibility (PRs #29, 31)
- x402 payment rail: agents can pay per-request
- Priority Provider (BYOK): use your own key + marketplace overflow
- CSV usage export for buyers
- Payout address feature for sellers
February-March 2026
- Settlement contract deployed on Base
- 187 tests (unit + integration)
- 10 provider integrations with automatic pricing discovery
- Multi-key support: up to 25 keys per wallet