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

# Quickstart

> Three lines in your agent's own code, one authorisation before the action, and a refusal you can read.

The whole integration is one call before a consequential action. There is no
runtime to adopt, no framework to standardise on, and no proxy in front of your
traffic.

<Steps>
  <Step title="Install">
    ```bash theme={"system"}
    pip install rotascale
    ```
  </Step>

  <Step title="Point it at your deployment">
    ```bash theme={"system"}
    export ROTASCALE_URL="https://rotagrant.internal.example"
    export ROTASCALE_API_KEY="rsk_..."
    ```

    See [Authentication](/authentication) for where the key comes from and what
    it is scoped to.
  </Step>

  <Step title="Ask before you act">
    ```python theme={"system"}
    from rotascale import Rotascale

    rs = Rotascale()

    decision = rs.authorize(
        grant_id="grt_01KZXYD62VG0",
        {"tool": ["payments.transfer"]},
        amount_minor=25_000_00,
        currency="EUR",
    )

    if decision.allowed:
        transfer(...)
    ```

    By default `authorize` raises on a refusal, so the unsafe path is the one
    you have to write on purpose rather than the one you get by forgetting to
    check a return value.
  </Step>
</Steps>

## What comes back

<CodeGroup>
  ```python Allowed theme={"system"}
  Decision(
      outcome="allow",
      allowed=True,
      reason="within grant",
      grant_id="grt_01KZXYD62VG0",
      ledger_id="led_01KZZWKNEPCJ",
  )
  ```

  ```python Refused theme={"system"}
  Exhausted:
    the call allowance on this grant is spent; retrying cannot help
  ```
</CodeGroup>

Six outcomes, not two. `allow`, `deny`, `exhausted`, `gated`, `review_sync` and
`review_async` are different findings that send an operator to different places,
which is why they are not collapsed into a boolean. See
[Outcomes](/concepts/outcomes).

## Nothing is enforced yet

A grant created today starts in `observe`. It records what the check *would*
have done and changes nothing. That is deliberate: run it against production for
a fortnight and find out what your agents are actually doing before you decide
whether to stop any of it.

<Tip>
  The first week of observe data reliably contains at least one behaviour nobody
  knew about. That finding is usually worth the integration on its own.
</Tip>

When you are ready, move one grant up the ladder. See
[Move up the ladder](/guides/move-up-the-ladder).

## Handling refusals properly

```python theme={"system"}
from rotascale import Blocked, Exhausted, Gated, ReviewRequired

try:
    rs.authorize(grant_id, {"tool": ["payments.transfer"]},
                 amount_minor=25_000_00, currency="EUR")
except Exhausted:
    # The allowance is spent. Retrying cannot help, and the agent is told so
    # rather than being left to loop.
    escalate_to_human()
except Gated:
    # Something untrusted entered this trajectory and the grant requires a
    # clean context for this action.
    escalate_to_human()
except ReviewRequired as exc:
    # A person has to decide. exc.review_id is the handle.
    park(exc.review_id)
except Blocked:
    # Policy said no.
    stop()
```

See [Errors](/errors) for the full hierarchy and what each one means.
