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

# needs_approval

> Does this call need a human? For a framework that asks before it invokes (SPEC-v0.5 §3.5).

`ctrlrun.needs_approval` — function, defined at `src/ctrlrun/adapter.py:408`

```python theme={null}
def needs_approval(control: Control, action: str, arguments: Mapping[str, Any], resource: str | None) -> bool
```

Does this call need a human? For a framework that asks before it invokes (SPEC-v0.5 §3.5).

The OpenAI Agents SDK's shape: the framework asks whether a tool call needs approval
*before* it invokes the tool, surfaces its own approval item, and invokes only after a human
answers. This answers that question and nothing else -- `True` iff the combined
SPEC-v0.3 §4.6 decision is `APPROVE`.

It is core's rather than each adapter's because the only way to write it in an adapter was
to build an `Action`, and `Action.principal` has no default: the principal would have come
from the framework's session, which is the one thing §4.2 forbids. Here it comes from
`Control.resolve_principal`, exactly as it does at every other entry point.

**It writes nothing**: no event, no receipt, no request, no reservation. A framework may
call its predicate more than once, and a predicate that left evidence behind would put a
proposal in the log for every time the framework wondered.

A `DENY` returns `False`, so the tool is invoked and `Control.execute` denies it with a
receipt, an `ACTION_DENIED` and the exception the caller catches. Refusing here would refuse
without evidence, and SPEC-v0.3 §4.3 is explicit that a denial with a principal to attribute
it to belongs in the evidence log.

`resource` is a template over `arguments`, as `@protect`'s is (v0.1 §5.1), and the policy's
`resource:` is used where none is given -- the same precedence `@protect` applies. It
matters: authority matches on resource patterns (SPEC-v0.3 §4.2), so a predicate that
skipped it would evaluate a different action from the one that runs.

## Next

* [Python API index](/reference/api/index).
* [Get started](/get-started/quickstart) · [Why](/why).


## Related topics

- [OpenAI Agents SDK tool approval](/cookbook/openai-agents-tool-approval.md)
- [Use the OpenAI Agents SDK adapter](/guides/openai-agents-adapter.md)
- [Adapters](/adapters.md)
- [Claims](/CLAIMS.md)
- [Python API](/reference/api/index.md)
