# Workloads in the API

A [workload](https://inference.ai.exchange/docs/workloads.md) is a repeat task that you save in the console. Each version of it has a name, its **profile**. You send the profile with a request, and the exchange labels the job with it. This page tells what the profile does in the API and what it does not do.

## What the profile is

A profile has the form `<workspace>.<workload>@v<n>`, such as `acme.invoice-extract@v2`. The workspace part is the slug of your workspace. The workload part is the slug of the workload. `n` is the version. A save that changes the task (the prompt, the model, the answer format or the settings) makes a new version, and so a new profile. A change to the cases makes no version. The workload page and the **Save as workload** dialog show the profile.

The API has no route to create or change a workload. Save it in the console. The routes behind the console are not public.

## Send a profile

The **Production** tab of a workload exports the code of the newest version in Python, TypeScript and curl. See [Watch the calls in production](https://inference.ai.exchange/docs/workloads.md#watch-the-calls-in-production). To write the call yourself, set `exchange.rank.workload_profile` in the `exchange` block of the request:

```bash
curl https://router.inference.ai.exchange/v1/chat/completions \
  -H "Authorization: Bearer $LIQUID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "messages": [{"role": "user", "content": "Extract the invoice: ACME, due 2026-11-01, 1200 USD"}],
    "response_format": {"type": "json_object"},
    "exchange": {"rank": {"workload_profile": "acme.invoice-extract@v2"}}
  }'
```

A client that cannot change the body sends the header `x-liquid-profile: acme.invoice-extract@v2`. A header applies only if the body and the preset do not set the rule. See [Purchasing rules](https://inference.ai.exchange/docs/rules.md).

If a request names no profile, the job takes the default profile of its API key.

## What the profile does not do

The exchange does not read the saved prompt, model or answer format from the profile. Your request must send its own `messages`, `model` and `response_format`. To make the same request as the console, copy them from the workload page.

A request does not need a profile that exists. The exchange checks only that the part before the first dot is not the slug of another workspace.

## What the profile changes

- **Label.** The exchange stores the profile on the job.
- **Task match.** When the profile names a workload of your workspace, the exchange compares the call with the version and keeps a mark on the job and the receipt. See [Task match](#task-match).
- **Offers that target a profile.** A provider can list `workload_profile_targets` on an offer.
  - An offer with targets competes only for jobs whose profile is in the list.
  - An offer with no targets competes for every job.
  - A provider can use this to hold a price for one buyer.
  - A dry run names the offers that a profile leaves out with `workload_profile_not_targeted`.

The profile does not change how the exchange ranks offers, and the task match changes no award and no charge. To rank by the chance of a good answer, send `exchange.rank.failure_risk_ranking`. See [Fewest redos](https://inference.ai.exchange/docs/rules.md#fewest-redos).

## Task match

When the profile names a version of a workload of your workspace, the exchange compares three parts of the call with the version: the system messages, the answer format and the settings that change the answer. The settings are the temperature, the output limit and the reasoning effort. The exchange compares digests, not the text. It does not compare the user messages or the model.

The receipt gives the result as `buyer.workload`:

```json
"workload": { "profile": "acme.invoice-extract@v2", "task": "drift_format" }
```

| `task`           | Meaning                                                              |
| ---------------- | -------------------------------------------------------------------- |
| `matches`        | The three parts are those of the version.                            |
| `drift_system`   | The system messages differ.                                          |
| `drift_format`   | The answer format differs.                                           |
| `drift_settings` | The temperature, the output limit or the reasoning effort differs.   |
| `no_version`     | The workload exists, but the exchange has no digest of this version. |

When more than one part differs, the mark names the first: the system messages, then the format, then the settings. A text answer and no answer format are the same format. A version that you saved less than 10 seconds ago can read `no_version`.

A call that drifts is served and charged as any other. The exchange marks it and never refuses it. A profile that names no workload of your workspace gets no mark, and the receipt has no `workload`.

## Run many takes

The console runs a workload by sending many requests, one lane at a time. To do the same, send each take with:

- The lane that you test. For one provider, send the model and set `exchange.candidates.providers.allow` to the provider. For a preset, a model or Auto, send its name as `model`. Auto also needs a price bound, such as `exchange.cap_usd`.
- `exchange.cache.response` set to `false`, so each take buys a new answer.
- The same `exchange.rank.workload_profile` on each take.

Then read `exchange.answer_checks` on each answer. See [Structured output](https://inference.ai.exchange/docs/structured-output.md).

## Refusals

| Status | Code                   | Meaning                                                                                                                                              |
| ------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `invalid_request`      | The profile starts with the slug of another workspace. `param` is `workload_profile`. Use a profile that starts with the slug of your own workspace. |
| 403    | `key_policy_violation` | The key has a preset with a different `exchange.rank.workload_profile`. Send the profile of the preset, or use another key.                          |

A [guardrail](https://inference.ai.exchange/docs/guardrails.md) cannot hold an `exchange.rank.workload_profile`.

## Next steps

- [Workloads](https://inference.ai.exchange/docs/workloads.md): save, run and read a workload in the console.
- [Purchasing rules](https://inference.ai.exchange/docs/rules.md): the `exchange` block and the headers.
- [Structured output](https://inference.ai.exchange/docs/structured-output.md): the check of each answer.
