Provider Keys — listing your key
The other side of the market: you already pay Fal, Wavespeed or ElevenLabs for capacity you are not fully using, and you list the key so other people's calls run on it at a discount you set.
This page is the end-to-end path through the API. The dashboard does the same thing with a form.
- ProbeIs this key valid? Key used once, never stored
- Price itdiscount_percent, 5–80
- Scope itCapabilities, refuse rules, caps
- Create the offerKey encrypted; listing goes live
1. Probe the key
Check a key before you commit it. The probe spends nothing — each provider's probe is a free, read-only endpoint — and the key is used once and never stored.
curl -X POST $SURPLUS_BASE_URL/resource/seller/keys/probe \ -H "Authorization: Bearer $SURPLUS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"resource_id":"elevenlabs","api_key":"<the provider key>"}'
{ "ok": true, "scopes": ["audio"] }A bad key answers 200 with "ok": false rather than an error — it is a verdict about your key, not a failure
of the request.
| Provider | What the probe reads |
|---|---|
fal | Unit prices for one endpoint. |
wavespeed | The account balance endpoint. |
elevenlabs | The model list. |
falWhat the probe reads · Unit prices for one endpoint.wavespeedWhat the probe reads · The account balance endpoint.elevenlabsWhat the probe reads · The model list.2. Create the offer
curl -X POST $SURPLUS_BASE_URL/resource/seller/offers \ -H "Authorization: Bearer $SURPLUS_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "resource_type": "elevenlabs", "api_key": "<the provider key>", "label": "Spare ElevenLabs capacity", "discount_percent": 50, "cap_daily_usd": 25, "policy": { "allow": ["elevenlabs.music", "elevenlabs.sound-effects"], "deny": [] }, "ack_persists_state": true, "ack_provider_terms": true }'
{ "id": "roff_01JC…", "resource_type": "elevenlabs", "discount_percent": 50, "active": true }The response never echoes the key, on any endpoint, including the listing read. It is encrypted under a KMS envelope and decrypted only at call time, in the worker that makes the upstream request.
resource_typestringrequiredfal,wavespeedorelevenlabs.api_keystringrequiredThe provider key. Key listings take
api_keyonly —api_secretis refused withapi_secret_unsupported.discount_percentintegerrequiredA whole number from 5 to 80, off the provider's own list price.
pricing_multiplieris refused here withpricing_multiplier_unsupported: a key listing is priced one way only.labelstringYours, and private to you. This is how you tell two listings of the same provider apart — see below.
cap_daily_usdnumberA spend ceiling in USD.
cap_weekly_usdandcap_monthly_usdwork the same way. A capable offer that has spent its cap is skipped, and the buyer seesseller_cap_reached.policyobject{ allow, deny }— the capabilities this key serves and the routes it refuses. Omit it and the listing stays on the provider's default template, resolved when it is read, so a later change to that template reaches every listing that never scoped itself.payout_addressstringWhere earnings go. Defaults to your organization's payout wallet.
ack_persists_statebooleanrequiredMust be
true. You are acknowledging that the provider may keep a record of buyers' calls on your account — on ElevenLabs it certainly will.ack_provider_termsbooleanrequiredMust be
true. Reselling your capacity is between you and the provider; check your plan's terms.
Both acknowledgements are mandatory — omit either and the request is refused acknowledgement_required. They
are not a formality: one is about your data, the other about your contract with the provider.
One active listing per key, per model
A credential may back one active offer per model, per base URL — enforced across every workspace and
organization, and not behind a feature flag. A second one is refused 409 duplicate_offer_for_credential,
naming the offer that already holds the claim.
The key is not the unit of uniqueness; the triple is — credential, base URL, model. Most production credentials legitimately serve many models, so keying on the credential alone would invalidate most of the market. The base URL is in there because the same key against a direct host and against an aggregator are different failure domains.
To change a live listing, update it. Do not create a second one.
curl -X PATCH $SURPLUS_BASE_URL/resource/seller/offers/roff_01JC… \ -H "Authorization: Bearer $SURPLUS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"discount_percent": 40, "cap_daily_usd": 50}'
Labels, not key hints
Listings carry no fragment of the key — not even a last-four hint, and the API does not return one.
That is a deliberate replacement rather than an omission. Several listings of one provider would all draw the
same provider name and the same …abcd tail, which makes pausing or repricing the right one guesswork. Your
own private label does that job properly, so give each listing one.
What you are agreeing to serve
Three checks decide every call, and the first refusal wins. You control the second and third.
A rule can only ever take away. There is no rule that adds a route, and nothing you write reaches past the fence — so the worst a typo can do is refuse too much. Access rules is the full reference, including the wildcard syntax.
Traits are claims until they are not
You can advertise properties of the account behind your key — zero retention, an EU region, a business plan —
and buyers can require them with X-SI-Require.
traitsVerified starts empty on every listing, however much you tick. A claim is not evidence, and the only
things that can move a trait to verified are our own prober and an operator reviewing what you supplied. Most
traits can only ever be Claimed, because there is nothing about someone else's account configuration we can
check from outside — and we would rather say that than imply a check we did not make.
A false claim removes the listing and holds its earnings.
Managing listings
| Endpoint | What it does |
|---|---|
GET /v1/resource/seller/offers | Your listings. Never includes the key. |
PATCH /v1/resource/seller/offers/{id} | Reprice, re-cap, re-scope, pause. |
DELETE /v1/resource/seller/offers/{id} | Delist. Releases the uniqueness claim. |
POST /v1/resource/seller/offers/{id}/purge | Delist and destroy the stored credential. |
DELETE /v1/resource/seller/offers | Delist everything. |
GET /v1/resource/seller/analytics | Earnings, and the demand your rules refused. |
GET /v1/resource/seller/offersWhat it does · Your listings. Never includes the key.PATCH /v1/resource/seller/offers/{id}What it does · Reprice, re-cap, re-scope, pause.DELETE /v1/resource/seller/offers/{id}What it does · Delist. Releases the uniqueness claim.POST /v1/resource/seller/offers/{id}/purgeWhat it does · Delist and destroy the stored credential.DELETE /v1/resource/seller/offersWhat it does · Delist everything.GET /v1/resource/seller/analyticsWhat it does · Earnings, and the demand your rules refused.Your refusals appear on your own analytics as demand your rules refused, priced at your rates, so you can see what a rule costs you before deciding to keep it.
Related
- Access rules — the three checks and the rule syntax, in full.
- Your first call — what a buyer experiences.
- Pricing — how a discount is applied and what it comes off.
- Provider key endpoints — the endpoint reference.
- Security and privacy — how your key is stored.