> ## 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.

# Quickstart

> Four steps to a labelled call. About five minutes.

```mermaid theme={null}
flowchart LR
  A["1 · Key"] --> B["2 · Base URL"] --> C["3 · Add task"] --> D["4 · Verify"]
```

Protégé is a managed API. No provider accounts, no upstream keys: one key, one
base URL, one extra field.

<Steps>
  <Step title="Get a key">
    ```bash theme={null}
    export PROTEGE_API_KEY="sk-protege-..."
    ```

    Use separate keys for production and staging, so staging traffic does not
    enter a task's production history.

    <Warning>
      The key bills real inference. Call Protégé from your server, never from a
      browser or a mobile binary.
    </Warning>
  </Step>

  <Step title="Point your client at Protégé">
    Change two lines. Everything else stays.

    <CodeGroup>
      ```python Python theme={null}
      from openai import OpenAI

      client = OpenAI(
          base_url="https://api.protege.sh/v1",
          api_key=os.environ["PROTEGE_API_KEY"],
      )
      ```

      ```typescript TypeScript theme={null}
      const client = new OpenAI({
        baseURL: "https://api.protege.sh/v1",
        apiKey: process.env.PROTEGE_API_KEY,
      });
      ```
    </CodeGroup>
  </Step>

  <Step title="Label the call">
    Add `task`. Name the job, not the prompt or the model.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.protege.sh/v1/chat/completions \
        -H "Authorization: Bearer $PROTEGE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "task": "invoice_extraction",
          "model": "deepseek-v4-flash",
          "messages": [{"role": "user", "content": "Extract the total."}]
        }'
      ```

      ```python Python theme={null}
      resp = client.chat.completions.create(
          model="deepseek-v4-flash",
          messages=[{"role": "user", "content": "Extract the total."}],
          extra_body={"task": "invoice_extraction"},
      )
      ```

      ```typescript TypeScript theme={null}
      const resp = await client.chat.completions.create({
        model: "deepseek-v4-flash",
        messages: [{ role: "user", content: "Extract the total." }],
        // @ts-expect-error - `task` is a Protégé extension
        task: "invoice_extraction",
      });
      ```
    </CodeGroup>

    <Note>
      The OpenAI SDKs drop fields they do not recognise. Python needs
      `extra_body`; TypeScript needs the cast. Other SDKs are covered in
      [Migrating](/migrate).
    </Note>
  </Step>

  <Step title="Verify it was labelled">
    The response echoes the scope it was attributed to:

    ```
    x-protege-project: default
    x-protege-workload: invoice_extraction
    ```

    If the workload comes back `main`, the `task` field was stripped by your SDK
    before it reached us. Log both headers alongside your own request IDs.
  </Step>
</Steps>

## Next

<CardGroup cols={3}>
  <Card title="Split a workflow" icon="scissors" href="/tutorials/split-a-workflow">
    Turn one expensive call path into labelled tasks.
  </Card>

  <Card title="Write an eval contract" icon="clipboard-check" href="/tutorials/write-an-eval-contract">
    Define correct, so a route can be gated on it.
  </Card>

  <Card title="Route a task down" icon="route" href="/tutorials/route-a-task-down">
    Move a task to a cheaper model without losing quality.
  </Card>
</CardGroup>
