The OpenAI Agents SDK tool is the caller. It reads an access token from run context and sends `Authorization: Bearer` to your API. `require_auth` on that API is the gate. The tool does not decide who the caller is, and it does not invent a subject.

Sample: [openai-agents/python](https://github.com/authdog/agents-samples/tree/main/openai-agents/python). Python 3.10+. Package `openai-agents==0.24.0` (`import agents`).

## Hold the token in context

```python
@dataclass(frozen=True)
class Caller:
    token: str

async def fetch_subject(token: str, client: httpx.AsyncClient, url: str) -> str:
    headers: dict[str, str] = {}
    if token:
        headers["Authorization"] = f"Bearer {token}"
    response = await client.get(url, headers=headers)
    response.raise_for_status()
    return str(response.json()["subject"])

def build_agent(client: httpx.AsyncClient, url: str) -> Agent:
    @function_tool
    async def whoami(ctx: RunContextWrapper[Caller]) -> str:
        """Return the Authdog subject for this caller. Do not invent one."""
        return await fetch_subject(ctx.context.token, client, url)

    return Agent(
        name="authdog-caller",
        instructions="Call whoami. Never invent a subject.",
        tools=[whoami],
    )
```

`Caller.token` is the access token from [the client-credentials exchange](/docs/agent-frameworks). The client secret stays in the process that minted it. The model sees the tool result, which is the subject string the API returned.

Run it by passing `context=Caller(token=...)` to `Runner.run`. `OPENAI_API_KEY` is required for that run. Proving the tool attaches the header does not require a model: call `fetch_subject` directly.

## The route the tool calls

```python
authdog = Authdog(public_key=public_key)

@app.get("/whoami")
async def whoami(user=Depends(authdog.require_auth)):
    return {"subject": subject_of(user)}
```

`Authdog` comes from `authdog.fastapi`. A missing or rejected token is `401` from this route, including when the tool sends an empty header because `Caller.token` is empty.

To act for a person, keep this route and this tool. Put an [RFC 8693 token](/docs/agent-frameworks#acting-for-a-person) in `Caller.token` so `sub` stays the user and `act` names the agent. The samples mint a `client_credentials` token whose subject is the agent.

## What you should see

1. `GET /whoami` with no token returns `401`.
2. The same call with a bearer userinfo accepts returns `{"subject": "<id>"}`.
3. `Runner.run(..., context=Caller(token=os.environ["AUTHDOG_ACCESS_TOKEN"]))` with instructions to call `whoami` prints that subject as the model output.

`pytest` covers the route and `fetch_subject`. It does not call `Runner`.

## Configure

| Variable | Process | Purpose |
| --- | --- | --- |
| `PK_AUTHDOG` | API | Public key for `require_auth`. |
| `WHOAMI_URL` | Agent | Absolute URL of `GET /whoami`. |
| `AUTHDOG_ACCESS_TOKEN` | Agent | Bearer to place in `Caller`. |
| `AUTHDOG_CLIENT_ID` / `AUTHDOG_CLIENT_SECRET` | Minting process | Only if you exchange a token yourself. |
| `OPENAI_API_KEY` | Agent | Only for `Runner.run`. |

```bash
uvicorn tool_api:create_app --factory --port 8000
```

> **Caution**
>
> Install `authdog-fastapi` from the pinned [web-sdk](https://github.com/authdog/web-sdk) commit in the sample `requirements.txt`, with the `fastapi` extra. Do not put the client secret in `Caller` or in the agent instructions.

## Related

| Read | To learn how to |
| --- | --- |
| [LangGraph](/docs/agent-frameworks/langgraph) | Protect the workflow the tool is calling |
| [Agent delegation](/guides/agent-delegation) | Exchange a user token when the agent acts for a person |
| [Agents quickstart](/docs/quickstarts/agents) | Register the client that mints `AUTHDOG_ACCESS_TOKEN` |
