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

# Basic Usage

> Learn the fundamentals of using the StackOne Python SDK for AI agents

## Quick Start

The most common pattern: create a toolset and give your agent search and execute tools. The agent discovers and runs tools on demand.

```python theme={null}
from stackone_ai import StackOneToolSet

# search_and_execute returns the tool_search + tool_execute meta tools, so search
# must be enabled on the toolset (pass search={} for defaults).
toolset = StackOneToolSet(search={"method": "auto"})

# Your agent gets 2 tools: tool_search + tool_execute
tools = toolset.openai(mode="search_and_execute", account_ids=["your-account-id"])
```

Pass `tools` to any OpenAI-compatible model and the agent will search for relevant tools, then execute them automatically. See the [OpenAI integration guide](/agents/python/frameworks/openai-integration) for a full agent loop.

## Fetch Tools

When you need specific tools instead of search and execute:

```python theme={null}
tools = toolset.fetch_tools(account_ids=["your-account-id"])
openai_tools = tools.to_openai()
```

<AccordionGroup>
  <Accordion title="Filtering by provider, action, or pattern">
    ```python theme={null}
    # Filter by providers
    tools = toolset.fetch_tools(
        providers=["hibob", "workday"],
        account_ids=["your-account-id"],
    )

    # Filter by actions with exact match
    tools = toolset.fetch_tools(
        actions=["hibob_list_employees", "hibob_create_employees"],
        account_ids=["your-account-id"],
    )

    # Filter by actions with glob patterns
    tools = toolset.fetch_tools(
        actions=["*_list_*"],
        account_ids=["your-account-id"],
    )

    # Combine multiple filters
    tools = toolset.fetch_tools(
        providers=["hibob"],
        actions=["*_list_*"],
        account_ids=["your-account-id"],
    )
    ```

    See [Tool Filtering](/agents/python/tool-filtering) for the full reference.
  </Accordion>

  <Accordion title="Multi-account usage">
    ```python theme={null}
    # Set accounts on the toolset for all subsequent calls
    toolset.set_accounts(["account-123", "account-456"])
    tools = toolset.fetch_tools()

    # Or pass account IDs per request
    tools = toolset.fetch_tools(account_ids=["account-123", "account-456"])

    # Loop over customer accounts dynamically
    customer_accounts = ["account-1", "account-2", "account-3"]

    for account_id in customer_accounts:
        tools = toolset.fetch_tools(
            actions=["workday_list_workers"],
            account_ids=[account_id],
        )
        employee_tool = tools.get_tool("workday_list_workers")
        if employee_tool:
            employees = employee_tool.call()
            print(f"Found {len(employees['data'])} employees")
    ```
  </Accordion>
</AccordionGroup>

## Tool Execution

<AccordionGroup>
  <Accordion title="Direct tool execution (for debugging)">
    Direct execution is useful for testing and debugging. In production, your agent framework handles tool calls automatically.

    ```python theme={null}
    tools = toolset.fetch_tools(account_ids=["your-account-id"])
    employee_tool = tools.get_tool("workday_list_workers")

    # call() with keyword arguments
    result = employee_tool.call(id="employee-123", include_details=True)
    print(result["data"])

    # execute() with dictionary payloads
    result = employee_tool.execute({"id": "employee-123", "include_details": True})

    # execute() with OpenAI-style arguments
    payload = {"arguments": {"id": "employee-123"}}
    result = employee_tool.execute(payload)
    ```
  </Accordion>

  <Accordion title="Error handling">
    ```python theme={null}
    from stackone_ai.models import StackOneError, StackOneAPIError

    try:
        result = employee_tool.call(id="employee-123")
        print("Success:", result)
    except StackOneAPIError as e:
        print(f"API error: {e.message}")
    except StackOneError as e:
        print(f"StackOne error: {e.message}")
    except Exception as e:
        print(f"Unexpected error: {e}")
    ```
  </Accordion>

  <Accordion title="File downloads (binary responses)">
    Actions that download a file (for example `googledrive_unified_download_file`, `documents_download_file`, or any `*_unified_download_file`) return raw bytes plus metadata, not parsed JSON. The SDK decides from the response `Content-Type`: JSON is parsed as usual, and anything else is treated as a file download. `call()` and `execute()` both return this dict directly (it isn't wrapped in a result object), so read its values with dict keys like `result["content"]`.

    ```python theme={null}
    tools = toolset.fetch_tools(actions=["googledrive_*"], account_ids=["your-account-id"])
    download = tools.get_tool("googledrive_unified_download_file")

    result = download.execute({"id": "file-id"})

    # result describes the file. Write the bytes straight to disk:
    with open(result["file_name"] or "download.bin", "wb") as f:
        f.write(result["content"])
    ```

    The returned dict, from both `call()` and `execute()`:

    | Key            | Type          | Description                                                                    |
    | -------------- | ------------- | ------------------------------------------------------------------------------ |
    | `content`      | `bytes`       | Raw file bytes. Not JSON-serializable (see note).                              |
    | `content_type` | `str`         | File MIME type (for example `application/pdf`), or `application/octet-stream`. |
    | `status_code`  | `int`         | HTTP status of the download response.                                          |
    | `headers`      | `dict`        | Response headers.                                                              |
    | `file_name`    | `str \| None` | Filename from `Content-Disposition` (RFC 5987 `filename*` aware), else `None`. |

    <Warning>
      `content` is raw bytes and is not JSON-serializable. If you forward tool results to an LLM (or anything that re-serializes to JSON), handle or strip the `content` key. For example, base64-encode it on the LLM-facing path.
    </Warning>
  </Accordion>
</AccordionGroup>

## Environment Configuration

```bash theme={null}
export STACKONE_API_KEY=your_api_key
```

Account IDs are passed per-request when fetching tools or via `toolset.openai(mode="search_and_execute", account_ids=[...])`.

## Next Steps

* [OpenAI Integration](/agents/python/frameworks/openai-integration) for building agents with function calling
* [LangChain Integration](/agents/python/frameworks/langchain-integration) for framework-based workflows
* [Tool Search](/agents/python/tool-search) for natural language tool discovery
* [Tool Filtering](/agents/python/tool-filtering) for advanced filtering patterns
