Skip to main content
Getting startedWhitelabel

Whitelabel & Reseller Setups

Two technical patterns for putting Surplus behind your own product. Let Surplus provision and meter one key per end customer, or run your own gateway in front and call Surplus with a small pool of high-capacity keys.

The right pattern depends on one question: who owns per-customer isolation and billing? If you want Surplus to be the system of record for each downstream customer, use Pattern A. If your gateway already owns customer identity, quotas, and billing, use Pattern B.

Where the customer boundary lives
  1. Pattern Aone Surplus key per customer
  2. Pattern Byour gateway, one Surplus org
In Pattern A each customer is a Surplus key, so Surplus enforces isolation and budgets. In Pattern B your gateway is the boundary and Surplus sees one tenant.

Pattern A: a Surplus key per customer

Best when you want Surplus to hold each downstream customer's budget and usage. You create an organization, then use an admin key to mint one scoped buyer key per customer through the /v1/keys provisioning API. Each key carries its own scope, budget, and usage attribution, enforced on the backend. The organization remains the billing account: every key spends from one prepaid balance, and the per-key budget is what bounds a customer (see Isolation, budgets, and caps).

Admin keys (inf_admin_...)

An admin key is a management key. It mints, scopes, rotates, and revokes buyer keys through /v1/keys, scoped to its own workspace.

An admin key holds the full capability vocabulary (key management, offers, usage and request-log reads, budgets, webhooks, and the rest). Two things it cannot do: run inference (its key type is rejected at the inference guard, independently of any capability it holds), and mint another admin key (only an organization owner or admin session can issue a management key). No money-moving route admits an admin key either, so in practice it provisions and reads but never spends or withdraws. If you have read older docs describing it as "key-management and usage-read only", that description is out of date.

An organization owner or admin creates the admin key from the dashboard or an owner/admin session. Treat it like a root credential: anyone holding it can provision and revoke every customer key in the workspace.

The provisioning API (/v1/keys)

Full CRUD plus rotate over the workspace's keys. Every route requires the keys.manage capability, which an admin key holds. Every write here requires an Idempotency-Key header (create, update, rotate, and revoke alike); omitting it is a 400 idempotency_key_required, not a silent success.

POST/v1/keys

Mint a buyer key for a customer, with an optional scope (models, providers, providers_blocked, endpoints, min_discount_pct, trusted_only) and budget. The inf_... secret is returned once.

GET/v1/keys

List the workspace's provisioned keys (redacted). GET /v1/keys/{id} reads one; an id outside your workspace is a 404.

PATCH/v1/keys/{id}

Re-scope, rename, or re-budget a customer's key.

POST/v1/keys/{id}/rotate

Rotate a customer's key: mints a successor that inherits the old key's scope, workspace, and budget, and retires the old one. The successor has a new id and a new secret, so distribute both. grace_hours (default 0, meaning revoke the old key immediately) keeps both keys live through a cutover window.

DELETE/v1/keys/{id}

Revoke a customer's key.

Provision a customer key
POST /v1/keys
Authorization: Bearer inf_admin_xxx
Idempotency-Key: 9f8c1e2b-...     # required
{ "name": "customer-8842" }
# → { "id": "01JQ...", "key": "inf_...", "key_prefix": "inf_9f8c1e2b", "key_type": "buyer" }

Isolation, budgets, and caps

  • Isolation is per key, and it is enforced by scope plus budget, not by separate funds. Every key in the organization draws on the same prepaid balance, so what stops one customer spending another's money is the key's budget (hard_stop is on by default), not a wall between accounts. A key minted without a budget can spend the whole organization balance, so set one at mint time for every customer key. Usage is attributed per key regardless.
  • Budgets nest. A key's budget sits inside its member, workspace, and organization budgets, and every enclosing scope must have room. See budgets and spend limits.
  • Key caps are counts of active keys, not request-rate limits:
Self-serve (/v1/buyer/keys, /v1/seller/keys)Cap · 25 active keys per wallet, per typeWhat it counts · Buyer and seller keys are counted separately.
Provisioning (/v1/keys)Cap · 200 active keys per workspaceWhat it counts · The admin-key surface for Pattern A.

If you expect more than a few hundred concurrent customer keys, prefer Pattern B or contact us about a dedicated organization.

Pattern B: your gateway in front

This is the right model when your gateway already owns per-customer isolation, quotas, and billing. Surplus does not need to know your customers. You call it with a small pool of high-capacity buyer keys under one dedicated organization, and reconcile cost back to your own accounts.

Rate limits and throughput

Two independent limits apply. For a gateway pooling keys behind one address, the aggregate per-IP ceiling can bind first.

Application rate limitCeiling · ~1,200 requests per minute on the OpenAI-compatible completion path (/v1/chat/completions, /v1/completions); the other inference surfaces sit on a comparable per-minute bucketScoped by · Your API keyOn breach · 429 with Retry-After. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
Edge WAF ruleCeiling · 6,000 requests per 5-minute window, i.e. about 1,200 per minuteScoped by · Your source IPOn breach · Blocked at the edge, before the application sees the request. No X-RateLimit-* headers and no JSON error body.

The per-IP rule has roughly the same average ceiling as one completion key, but it counts every request from an address regardless of how many keys or routes those requests use. A gateway calling Surplus from one NAT address therefore tops out near 1,200 requests per minute on average no matter how its traffic is split across keys.

So a high-volume integration scales on two axes, and you need both:

  • Spread egress IPs. This is what actually lifts the binding ceiling for a gateway. Traffic split across N source addresses gets N times the per-IP allowance.
  • Pool keys. Once you are past the per-IP ceiling, several keys lift the per-key ceiling the same way. Under a dedicated org you provision these through the admin key. Pooling keys behind a single IP buys you nothing.
  • Plan high volume with us. Both ceilings are fixed guardrails rather than per-account tuning, so for sustained reseller traffic provision a dedicated organization and tell us your expected throughput and your egress addresses. We can then confirm upstream capacity and the per-IP allowance ahead of launch.

Beyond the rate limit, real throughput is bounded by your prepaid balance (a request is rejected if the balance cannot cover its estimated maximum cost) and upstream provider capacity for the models you route to. Requests are served by horizontally scaled fleets.

The rate limits documented on Seller Endpoints are a separate set of buckets covering the seller management routes (offers, discovery, key issuance). The limits above are what bound buyer throughput.

Automatic provider failover

You do not build failover yourself. The marketplace routes each request to the cheapest healthy seller for the model, marks a seller unhealthy on failure, and backs off, so traffic moves to the next seller automatically. You can also register your own provider keys as priority or fallback providers if you want your own capacity tried first or used as a safety net.

Reconciling Surplus cost against what you bill

Most requests expose their cost inline so you can attribute it to the downstream customer your gateway is billing. The inline figure is best-effort and wire-specific, so treat it as a fast path and the request log as the authority:

x-si-buyer-cost-micro response headerWhat it gives you · Non-streamed responses only. The buyer cost of that request in integer micro-USD, also placed on usage.buyer_cost_micro in the body. Omitted entirely when the charge is not known at response time, rather than sent as a misleading 0.
usage.buyer_cost_micro in the final stream eventWhat it gives you · Streamed responses only, because HTTP headers freeze at the first byte. It rides the terminal usage event, so it requires stream_options: {"include_usage": true} and an upstream that actually emits usage. If either is missing, or the cost cannot be computed, the event is passed through untouched rather than failing the stream.
GET /v1/requests and GET /v1/requests/{id}What it gives you · The per-request log (model, tokens, cost, status). The list needs the requestlog.read capability; the per-request detail is also readable by the inf_... key that made the request, so a key can pull its own settlement and cost rows.
GET /v1/buyer/me and GET /v1/buyer/usage/exportWhat it gives you · Usage stats, and a CSV of your usage records for periodic reconciliation. The export is bounded at 50,000 rows (or a lower limit you pass) and is not guaranteed complete: a capped response carries x-si-export-truncated: true. Narrow the from/to window and page through it, or use the explorer below.
POST /v1/analytics/explorerWhat it gives you · For high-volume reconciliation, an ad-hoc warehouse query over your own spend, grouped how you ask. Poll GET /v1/analytics/explorer/{queryId} for the result, or pull it as CSV from GET /v1/analytics/explorer/{queryId}/export. Needs the finops.read capability.

Use the inline figure for real-time margin where it is present, and reconcile against GET /v1/requests on a schedule. For a request whose cost never appeared inline (a stream without usage, or a charge not yet known), GET /v1/requests/{id} is the authoritative per-request record. Never infer a zero cost from a missing header or trailer.

Keep a balance buffer

Because a request is rejected when the balance cannot cover its estimated maximum, keep a buffer above your steady-state burn and enable automatic top-up so a busy hour never drains you to zero mid-stream.

A dedicated org for volume

There is no separate technical "reseller tier." Capacity is architectural (horizontally scaled fleets) and everyone runs against the same guardrails: the per-key and per-IP rate limits above, and your prepaid balance. There is no per-account throttle to raise, so a reseller is not given a bigger bucket than anyone else. What a high-volume reseller gets is a dedicated organization so usage, billing, and keys are cleanly separated from everyone else, plus a direct line to plan capacity and the per-IP allowance before launch.

Create one yourself in the dashboard: open the organization menu at the top of the sidebar and choose New organization. If you expect sustained volume, tell us your expected throughput and the models you plan to route through the Enterprise contact form or support@surplusintelligence.ai, so we can plan capacity.

Reselling and the Terms

Reselling is allowed under §8 of the Terms of Service, on the conditions it sets out.