# Sell with bring your own key (BYOK)

You can sell models that you reach through your own cloud account, such as Claude on Amazon Bedrock. You keep the account and pay the cloud. You run no serving stack. To use your own cloud account for your own requests, see [Bring your own key (BYOK)](https://inference.ai.exchange/docs/byok.md).

## How it works

1. You connect a cloud key to your provider. The exchange seals it.
2. You register a deployment for each model to sell, with `byok` set to `true`.
3. You quote a price for each deployment on an open session.
4. An operator lists the deployments. See [Deployments](https://inference.ai.exchange/docs/provider-models.md).
5. A buyer sends a request. The ranking picks your offer by price. The BYOK engine calls the cloud with your key and returns the answer.
6. The exchange settles on the tokens that the cloud counted. You pay the cloud. The exchange pays you your price, minus its fee.

This is a normal offer with a **BYOK** badge. You quote, and the exchange serves. Settlement is as for any provider. See [Serve and get paid](https://inference.ai.exchange/docs/provider-dispatch.md).

## What you need

- An admitted provider. See [Become a provider](https://inference.ai.exchange/docs/provider.md).
- An account at Anthropic, Amazon Bedrock, Google Vertex AI or Azure OpenAI with access to the models to sell.
- Cloud terms that allow you to resell its models. The exchange does not check this.
- A program that holds a provider session and sends quotes. See [Quote over the session](https://inference.ai.exchange/docs/provider-quoting.md).

The kinds of key, the secret fields and the rules for each cloud are the same as for a buyer's key. See [Clouds](https://inference.ai.exchange/docs/byok.md#clouds) and [Connect a key](https://inference.ai.exchange/docs/byok.md#connect-a-key). A key belongs to one provider.

## Connect a key

Open your provider in the console and add a cloud account. Anyone who can manage the provider can do this, with a recent sign-in. Only a Clerk session works. A provider key or CLI token cannot connect a cloud key.

The console calls `POST /v1/account/providers/{provider_id}/byok/grants`:

```json
{
  "upstream": "anthropic",
  "kind": "anthropic_api_key",
  "grant": { "api_key": "sk-ant-..." }
}
```

| Request (under `/v1/account/providers/{provider_id}/byok/`) | Effect                                                                                                                                                                                       |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET grants`                                                | Lists the keys, revoked ones included. A person with read access can call it. No response holds a secret.                                                                                    |
| `POST grants`                                               | Connects a key. The answer is `201`.                                                                                                                                                         |
| `PUT grants/{id}` with `{"grant": …}`                       | Replaces the secret. The kind and region stay.                                                                                                                                               |
| `DELETE grants/{id}`                                        | Revokes the key and deletes the sealed secret. The answer is `204`.                                                                                                                          |
| `POST grants/{id}/test`                                     | Asks for a test. The answer is `202`. The engine makes one small call and records `last_test_ok`. A Bedrock key without `bedrock:GetFoundationModelAvailability` shows `missing_permission`. |
| `PATCH grants/{id}`                                         | Changes `retention_claim` and `model_scope`.                                                                                                                                                 |
| `GET models`                                                | Lists the models that the live keys offer.                                                                                                                                                   |

- A grant of another provider gets `404 no_grant`.
- You cannot register a handle that starts with `byok-`. The exchange keeps those for buyers' keys.
- A seller's key has no `api_key_scope`. The route refuses it with `400 invalid_settings`.

## Choose the models to sell

A new key shows its models a few seconds after you connect it. `GET …/byok/models` returns, for each model:

| Field              | Meaning                                                          |
| ------------------ | ---------------------------------------------------------------- |
| `upstream`         | The cloud.                                                       |
| `name`             | The name on the exchange.                                        |
| `upstream_model`   | The id at the cloud. The engine calls the cloud with this id.    |
| `max_input_tokens` | The context window for the key. Absent when the key states none. |

A model that you do not register is not on the book. To sell only some models, set `model_scope` of the key to their `upstream_model` ids.

## Register a deployment

Register each model as in [Deployments](https://inference.ai.exchange/docs/provider-models.md). Set `upstream_model` to the cloud id and `byok` to `true`:

```bash
curl https://router.inference.ai.exchange/v1/provider/models \
  -H "Authorization: Bearer sk-liquid-..." \
  -H "Content-Type: application/json" \
  -d '{
    "deployment_name": "claude-opus-5-5",
    "upstream_model": "<upstream_model from the list>",
    "byok": true,
    "creator": "anthropic",
    "precision": "bf16",
    "region": "us-east-1",
    "max_input_tokens": 200000,
    "input_modalities": [{ "type": "text" }],
    "output_modalities": [{ "type": "text" }]
  }'
```

With `byok` set to `true`, the exchange ignores the dispatch URL of your session for this deployment.

- A registration without `byok` keeps the current delivery. Send `"byok": false` to serve with your own agent again.
- The exchange refuses `byok` with `409 no_key_for_model` unless a live key of your provider lists `upstream_model`. Connect the key, wait a few seconds, and register again.
- Set `max_input_tokens` to the limit that the cloud gives your account. Set `precision` to the value the cloud states, or the nearest true value. Set `region` to the cloud region of the key. The `exchange.privacy.regions` rule uses it.

## Quote your models

Your agent holds the session and sends the quotes. The frames are the same as for any provider. Read [Quote over the session](https://inference.ai.exchange/docs/provider-quoting.md) first. What differs here:

1. **Use an open session.** `liquid`, `openai` and `anthropic` sessions can all quote BYOK deployments. Do not log in again, because a second login replaces the first session and removes its offers. With no session, see [A provider that sells only BYOK](#a-provider-that-sells-only-byok).
2. **Read `listed`.** Quote only the `LISTED` and `UNLISTED` deployments in it.
3. **Send one `quote` for each deployment.** Use the target `{ "kind": "registered", "model_id": "…" }` and a higher `revision` each time.
4. **Keep the session open.** Quote again before `valid_for_ms` ends.

If you change `byok` on a resting deployment, quote it again.

```json
{
  "type": "quote",
  "target": { "kind": "registered", "model_id": "3f0c…" },
  "pricing": [
    { "billable": "input_text", "unit": "mtok", "usd": 3.3 },
    { "billable": "output_text", "unit": "mtok", "usd": 16.5 }
  ],
  "fixed_fee": 0,
  "service_class": { "ttft_ms": 3000, "completion_ms": 60000 },
  "supported_features": ["text", "tools", "input.image"],
  "retention_policy_supported": ["allowed"],
  "synthetic": false,
  "revision": 1
}
```

Rates are USD per million tokens.

| Field                                      | What to set                                                                                                                                  |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `input_text`, `output_text` in `pricing`   | Your price, above the cloud price by your margin. A job that costs you more than you quote is your loss.                                     |
| `cache_read`, `cache_write_*` in `pricing` | Quote a cache rate only when the model and cloud report cached tokens. Test one job.                                                         |
| `service_class`                            | Deadlines above what the cloud takes for your region and quota. A missed deadline is still paid, but the exchange marks the deployment slow. |
| `supported_features`                       | Only what the model, cloud and engine support. A feature that the engine cannot send to the cloud fails the job.                             |
| `retention_policy_supported`               | Match the `retention_claim` of your keys. See [Retention claim](#retention-claim).                                                           |
| `synthetic`                                | `false`. A production exchange refuses `true`.                                                                                               |

The registration sets the model, region, context limit and tokenizer. A quote cannot change them.

### Quote only what you can serve

The exchange has no capacity field. If a key reaches the cloud quota, the job fails and counts against the health of your deployment. To stop awards, send `withdraw_quote` or `mass_withdraw`, and quote again when you have room. Withdraw a model when:

- You revoked the last live key that lists it.
- The cloud retired the model or removed your access.
- The `model_scope` of your keys no longer lists it.

Keep the session open even when it serves no BYOK job. If it closes, the exchange removes your offers.

### A provider that sells only BYOK

Leave `declared_endpoint` out when you register. Log in with the `byok` dialect and no `dispatch_url`:

```json
{
  "type": "login",
  "slug": "nimbus",
  "api_key": "sk-liquid-...",
  "dispatch_dialect": "byok"
}
```

- The exchange accepts this login only for a provider with a live cloud key. Otherwise it answers `login_reject`.
- After `login_ack`, every job of this session goes to the BYOK engine.
- A provider with no registered endpoint cannot log in with the `liquid`, `openai` or `anthropic` dialect.

`quote_ack` means that the offer rests. For a `quote_reject`, see [Quote over the session](https://inference.ai.exchange/docs/provider-quoting.md#send-a-quote).

## What the engine does with a job

- It tries the live keys of your provider oldest first. It goes to the next key only when the cloud refuses the credentials. It skips a key whose model scope does not list the model.
- It streams the cloud answer back as the cloud sends it.
- It skips a key for a model for ten minutes after the cloud answers 404 for that model.
- It reads keys through replication. A revoke stops service at once. If replication fails, the engine exits instead of serving a revoked key.
- After the cloud sent output, the job cannot fall back to another offer. Before output, the next offer in the ranking can take it.

## Retention claim

Set `retention_claim` of each key to what your cloud agreement says:

| Value     | Meaning                                                                                                             |
| --------- | ------------------------------------------------------------------------------------------------------------------- |
| `default` | You state nothing. Only Bedrock counts as zero retention, except Claude Fable and OpenAI models other than gpt-oss. |
| `zero`    | The cloud does not keep prompts.                                                                                    |
| `keeps`   | The cloud keeps prompts. The offer is never `none` for retention.                                                   |

An offer declares zero retention only when every key that can serve it has a `zero` claim. The exchange does not check your claim. See [Provider claims](https://inference.ai.exchange/docs/provider-claims.md).

## Sell one model in two ways

You can serve the same model with your own agent and with BYOK. Register two deployments with different names. Both share `model`, so both are in the market of the model, each with its own quote, price and health record.

| Field             | Your agent                  | BYOK                             |
| ----------------- | --------------------------- | -------------------------------- |
| `deployment_name` | `claude-opus-5-5`           | `claude-opus-5-5-byok`           |
| `model`           | `claude-opus-5-5`           | `claude-opus-5-5`                |
| `byok`            | absent or `false`           | `true`                           |
| `upstream_model`  | The id that your agent uses | The id that your cloud key lists |

The ranking picks the cheaper offer that the buyer's rules allow. To move a deployment from one delivery to the other, send `byok` on its registration and quote again.

## Stop selling

To pause one deployment, see [Deployments](https://inference.ai.exchange/docs/provider-models.md). To stop all service from a key, revoke it. To sell fewer models, change `model_scope`.
