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

# Create a private worker

> Register typed Python functions as governed tools over an outbound-only worker connection.

Workers keep database and API access inside your infrastructure. They make
outbound HTTPS requests to AIR; you do not expose an inbound worker port.

<Steps titleSize="h2">
  <Step title="Install the SDK">
    ```bash theme={null}
    python -m pip install papermap-air
    ```
  </Step>

  <Step title="Declare a typed tool">
    Create `tools.py`:

    ```python theme={null}
    from air import tool


    @tool(timeout=30)
    def lookup_order(order_id: str, context: dict) -> dict:
        """Look up an order for the current tenant."""

        tenant_id = context["tenant_id"]
        # Query your system and verify the order belongs to tenant_id.
        return {
            "order_id": order_id,
            "tenant_id": tenant_id,
            "status": "shipped",
        }
    ```

    Type annotations become the JSON input schema. The first docstring line is
    the description shown during review. Every tool must return a dictionary.
  </Step>

  <Step title="Issue a worker key">
    Open **Settings**, select **Issue worker key**, and store the value in an
    environment variable or secret manager.

    <Frame caption="Worker credentials are separate from application credentials.">
      <img src="https://mintcdn.com/papermap/6FYSF5TTvsKzeVI6/air/images/project-keys.png?fit=max&auto=format&n=6FYSF5TTvsKzeVI6&q=85&s=3ad49dcce41c9d8e1ba75fbda5fa6d85" alt="AIR settings showing separate app and worker key controls" width="2880" height="1800" data-path="air/images/project-keys.png" />
    </Frame>
  </Step>

  <Step title="Start the worker">
    ```bash theme={null}
    export AIR_BASE_URL="https://dev.airapi.papermap.ai"
    export AIR_WORKER_KEY="<your-worker-key>"
    export AIR_WORKER_ENV="staging"

    air worker start --module tools
    ```

    The process registers its manifest and begins long-polling for tool calls.
  </Step>

  <Step title="Review the discovered tool">
    Open **Tools**. A newly discovered capability starts **awaiting approval**;
    registration never grants permission by itself.

    <Frame caption="AIR has discovered one worker tool but no agent can call it yet.">
      <img src="https://mintcdn.com/papermap/6FYSF5TTvsKzeVI6/air/images/pending-tool.png?fit=max&auto=format&n=6FYSF5TTvsKzeVI6&q=85&s=97c1d6ff4df604774baa2f43e5b76217" alt="AIR Tools screen showing lookup_order awaiting approval" width="2880" height="1800" data-path="air/images/pending-tool.png" />
    </Frame>
  </Step>

  <Step title="Inspect and enable it">
    Select **Review**. Confirm the source, description, timeout, generated input
    schema, and whether it needs write access. Then select **Enable tool**.

    <Frame caption="Review the generated schema and explicitly grant write capability only when required.">
      <img src="https://mintcdn.com/papermap/6FYSF5TTvsKzeVI6/air/images/approve-tool.png?fit=max&auto=format&n=6FYSF5TTvsKzeVI6&q=85&s=480fa2c146f0efe5a0e0e0c593f05654" alt="AIR tool review dialog showing lookup_order schema and read-only approval" width="2880" height="1800" data-path="air/images/approve-tool.png" />
    </Frame>
  </Step>

  <Step title="Allow it on an agent version">
    Create a new agent version, open **Advanced — fallback models, tools, and
    limits**, add `lookup_order`, test the version, and activate it. Enabling a
    project tool does not automatically add it to existing agent versions.
  </Step>
</Steps>

<Note>
  Registration answers “what can this worker do?” Tool approval answers “does
  this project trust it?” The agent version answers “may this agent call it?”
</Note>
