> ## Documentation Index
> Fetch the complete documentation index at: https://docs.protege.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Projects and workloads

> Two levels of scope. A project is a product surface; a workload is one stable call site inside it.

Every request is attributed to a **project** and a **workload**.

```
organization
  └── project   "support"
        ├── workload  "main"
        ├── workload  "intent"
        └── workload  "draft-reply"
```

A project is a product surface. A workload is one stable call site within it,
and it is the unit the product actually operates on: capture is toggled per
workload, sampling is set per workload, and routes attach to workloads.

## Declaring scope

Two optional headers. Both have defaults, so an unscoped request still works.

<ParamField header="x-protege-project" type="string" default="default">
  Project slug. Must match `^[a-z0-9-]{1,63}$`: lowercase letters, digits and
  hyphens.
</ParamField>

<ParamField header="x-protege-workload" type="string" default="main">
  Workload name. Must match `^[a-z0-9_-]{1,63}$`: lowercase letters, digits,
  hyphens and underscores.
</ParamField>

Both are provisioned on first use. There is no create call to make before you
can send traffic.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.protege.sh/v1/chat/completions \
    -H "Authorization: Bearer $PROTEGE_API_KEY" \
    -H "x-protege-project: support" \
    -H "x-protege-workload: draft-reply" \
    -H "Content-Type: application/json" \
    -d '{"model":"auto","messages":[{"role":"user","content":"Draft a reply."}]}'
  ```

  ```python Python theme={null}
  resp = client.chat.completions.create(
      model="deepseek-v4-flash",
      messages=messages,
      extra_headers={
          "x-protege-project": "support",
          "x-protege-workload": "draft-reply",
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const resp = await client.chat.completions.create(
    { model: "deepseek-v4-flash", messages },
    { headers: { "x-protege-project": "support", "x-protege-workload": "draft-reply" } },
  );
  ```
</CodeGroup>

## `task` is the same thing

The body field `task` is honoured as an alias for the workload name:

```json theme={null}
{ "task": "draft-reply", "model": "deepseek-v4-flash", "messages": [] }
```

`task` and workload are the same idea, the name of the repeated job. The header
wins when both are present, because it needs no body parse.

<Note>
  Use whichever fits your client. `task` is easier from an SDK that makes custom
  headers awkward; the header is easier when you already set headers per call
  site and do not want to touch the request body.
</Note>

## Reading it back

Every response echoes the scope it was attributed to:

```
x-protege-project: support
x-protege-workload: draft-reply
```

Log both. If they come back `default` and `main` when you expected otherwise,
the header was dropped or the `task` field was stripped by your SDK before it
reached us.

## Naming

The rules that matter are not the character class.

**Name the call site, not the implementation.** `draft-reply` survives a prompt
rewrite and a model swap. `gpt4-draft-v3` does not, and when it changes you lose
the history attached to it.

**Keep the name stable.** It is the join key for captures, cost and routing.
Renaming starts the history over.

**One workload per thing that can be correct or incorrect in one way.** If you
cannot say in a sentence what a right answer looks like, the workload is too
broad.

<AccordionGroup>
  <Accordion title="Too broad: one workload for a whole agent">
    Classifying an intent, fetching an account, drafting a reply and checking tone
    have different inputs, different failure modes and very different costs.
    Bundled, they average out and the cheap parts subsidise the expensive ones
    invisibly. Split them into one workload each, inside one project.
  </Accordion>

  <Accordion title="Too narrow: one workload per customer">
    Splitting per tenant fragments the capture corpus, so no workload accumulates
    enough history to earn a cheaper route. Keep one workload and separate tenants
    with the standard `user` field.
  </Accordion>
</AccordionGroup>

## Capture

Capture is a per-workload setting, with a sample rate. Turning it off stops the
recording for that workload without touching routing or your integration.

<Warning>
  A capture toggle can take up to 60 seconds to reach the gateway, because the
  setting is cached on the request path. Treat it as a recording switch, not as
  an instant privacy control.
</Warning>

<Card title="What gets captured" icon="database" href="/concepts/capture">
  The fields recorded per request, and what they are used for.
</Card>
