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

# Call Tools Without a Chat Thread

> Run any tool from the Genesis global registry — including Document Index actions — over plain HTTP, with no chat thread or LLM involvement.

## Overview

The **`tool_call`** endpoint lets you invoke any tool function from the
Genesis global registry as a plain HTTP request. There is no chat
thread, no LLM, and no agent decision step — you supply the tool name
and arguments, Genesis runs the tool and returns its result.

Use this when you already know which tool to run and want deterministic,
scriptable execution. Use a [chat thread](/docs/Genesis-API/Chat-Endpoints)
when you want an agent to *decide* which tools to call based on natural
language.

| You want… | Use… |
| - | - |
| The agent to decide what tools to call from natural language | [Chat with agents](/docs/Genesis-API/Chat-Endpoints) |
| To run one specific tool with known arguments, headlessly | `POST /api/tool_call` |
| To list all available tool groups | `GET /api/tool_call/list` |
| To inspect one visible tool function's callable schema before invoking it | `POST /api/tool_call/schema` |
| Topic-specific REST endpoints (with typed schemas) | The relevant API page, e.g. [Document Index Endpoints](/docs/Genesis-API/Document-Index-Endpoints) |

The `tool_call` endpoint runs against the **same global tool registry**
that bots use, so most tools the bots can use — Document Index management,
scheduler, lineage, Snowflake actions, file operations, MCP tools, etc.
— are callable here. Authentication and per-user `Permission` checks
still apply.

<Note>
  Authenticate with a Personal Access Token (PAT). See
  [Personal Access Tokens](/docs/User-Guide/Personal-Access-Tokens) for
  how to obtain one.

  ```bash theme={null}
  export GENESIS_URL="https://<your-genesis-host>"
  export GENESIS_PAT="gp_replace-me"
  AUTH=(-H "Authorization: Bearer $GENESIS_PAT")
  ```
</Note>

<Tip>
  For Python scripts, notebooks, or CI jobs, you can wrap the same PAT-backed
  APIs with `genesis_sdk` or the product `genesis` CLI instead of hand-writing
  every HTTP request.

  Set `GENESIS_API_BASE_URL` and `GENESIS_API_TOKEN`, then use
  `from genesis_sdk import Genesis`, `Genesis.from_environment()`,
  `gx.query.rows(...)`, `gx.tools.list()`, or `gx.tools.call(name, args)`.
  CLI equivalents include `genesis query --connection ... --sql ...`,
  `genesis tools list`, `genesis tools schema <name>`, and
  `genesis tools call <name> --json-args '{"key":"value"}'`.
</Tip>

## Endpoints

| Method | Path | Description |
| - | - | - |
| `GET` | `/api/tool_call/list` | List registered tool groups and counts. |
| `POST` | `/api/tool_call/descriptors` | Return bounded LLM-facing descriptors for visible tool groups and/or named tools. |
| `POST` | `/api/tool_call/schema` | Return the LLM-facing schema for one visible tool function. |
| `POST` | `/api/tool_call` | Invoke one tool function by name. |

### `GET /api/tool_call/list`

Discovery endpoint. Returns the registered tool **groups** plus the total
number of tool functions visible to the caller. Pass `max_groups` to return a
bounded page: omitted groups are not serialized, while `total_groups` and
`total_tools` still count every visible group and tool. Use `offset` with
`max_groups` to request a later page without serializing the groups before it;
the response echoes the served `offset`. A bounded response includes
`groups_returned` and `truncated: true` when groups were omitted.

```bash theme={null}
curl -sS "${AUTH[@]}" "$GENESIS_URL/api/tool_call/list?max_groups=2&offset=2"
```

```json theme={null}
{
  "success": true,
  "response": {
    "total_tools": 186,
    "total_groups": 58,
    "offset": 2,
    "groups_returned": 2,
    "truncated": true,
    "tool_groups": [
      {
        "name": "document_index_tools",
        "description": "Document index management",
        "tool_count": 1
      },
      {
        "name": "scheduler_tools",
        "description": "Scheduler / cron-like jobs",
        "tool_count": 4
      }
    ]
  }
}
```

This endpoint lists **groups**, not individual function names. The full
tool inventory is documented in the
[Genesis Data Agent's Toolkit](/docs/Genbot-Toolkit/Tools-Overview) —
each toolkit page lists the tool function names you can pass as `func`.

`max_groups` is an optional integer query parameter with a minimum of `1`.
`offset` is an optional integer query parameter with a minimum of `0`; it skips
that many visible groups before serializing the page and is echoed in the
response when supplied. Use both parameters to page through the registry.
Omit `max_groups` to return the complete visible group listing.

### `POST /api/tool_call/descriptors`

Return LLM-facing descriptors for all tools in selected visible groups and/or
for individually named tools. This is a discovery-only operation; it does not
invoke tools or change which tools a session can use. Group and name requests
are bounded at `50` groups and `200` names per request.

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{"groups": ["document_index_tools"], "max_group_descriptors": 3, "exclude_names": ["_document_index"]}' \
  "$GENESIS_URL/api/tool_call/descriptors"
```

#### Request body

| Field | Type | Required | Description |
| - | - | - | - |
| `groups` | array of strings | No | Visible tool groups whose tools should be described. Maximum `50` entries. |
| `names` | array of strings | No | Individual visible tool names to describe. Maximum `200` entries. |
| `max_group_descriptors` | integer or `null` | No | Maximum number of descriptors built from the requested groups. Minimum `0`; omit it to describe every matched group tool. Named tools are always described in full. |
| `exclude_names` | array of strings | No | Tools to leave out of the groups' answer entirely: they are not built, named, or counted against `max_group_descriptors`. Maximum `200` entries. Defaults to `[]`. |

At least one of `groups` or `names` is required. The response includes
`descriptors`, `count`, `matched_tool_names`, `total_matched`, `groups`,
`unknown_groups`, `unknown_names`, and `unavailable_groups`. With a group
bound, `matched_tool_names` still names every tool matched by the groups, so a
caller can request descriptors omitted from the bounded response by name.
`exclude_names` applies to tools expanded from `groups`; individually requested
`names` are still described. `unavailable_groups` identifies groups deliberately
not loaded in this build; it is distinct from `unknown_groups`.

### `POST /api/tool_call/schema`

Discovery endpoint for one visible tool function. Use this when your client
already knows the tool name and needs the exact callable schema before sending
`POST /api/tool_call`.

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{"name": "_document_index"}' \
  "$GENESIS_URL/api/tool_call/schema"
```

```json theme={null}
{
  "success": true,
  "response": {
    "schema": {
      "name": "_document_index"
    },
    "tool_groups": ["document_index_tools"]
  }
}
```

#### Request body

| Field | Type | Required | Description |
| - | - | - | - |
| `name` | string | Yes | Tool function name. Identifier-like; up to 200 chars; dots are allowed. The tool must be visible to the caller. |
| `resolve_only` | boolean | No | Default `false`. When `true`, return only whether the tool name resolves and skip building the LLM-facing schema. |

The response returns `response.schema` plus `response.tool_groups`. When a tool
publishes a richer descriptor, Genesis returns that full LLM-facing schema;
otherwise the fallback schema includes at least the tool name.

Set `resolve_only` to `true` when you need to check whether a tool exists before
acting on it without requesting its schema. A successful response is:

```json theme={null}
{
  "success": true,
  "response": {
    "resolved": true
  }
}
```

`POST /api/tool_call/schema` applies the same tool-group visibility and
permission checks as execution. It returns `403` when the caller lacks the
required permission and `404` when the tool name is not registered or visible
to the caller. These status codes are unchanged when `resolve_only` is `true`.

### `POST /api/tool_call`

Invoke a tool function:

```http theme={null}
POST /api/tool_call
Authorization: Bearer gp_…
Content-Type: application/json

{
  "func": "_document_index",
  "func_args": {
    "action": "LIST_INDICES"
  }
}
```

```json theme={null}
{
  "success": true,
  "response": {
    "Success": true,
    "Indices": ["wiki", "support_docs"]
  }
}
```

#### Request body

| Field | Type | Required | Description |
| - | - | - | - |
| `func` | string | Yes | Tool function name. Identifier-like; up to 200 chars; dots are allowed. The exact same name a bot would emit when calling the tool. |
| `func_args` | object | No | Keyword arguments for the tool. Keys must be identifier-like (≤100 chars). Ordinary string values are limited to 50 000 chars; file payload arguments are limited to 2 097 152 chars. Most strings are trimmed, except verbatim file-content arguments listed below. Scoped dbt/Jinja templates and raw HTML are rejected unless the target input is on a tool-specific allowlist. |
| `agent_id` | string or null | No | Optional agent persona for this call. The caller must be allowed to access that agent; authorization is checked for both the caller and agent. Tools that record which agent acted use this persona as the acting-agent identity. |
| `caller_name` | string or null | No | Optional readable name for the outside calling agent (up to 80 characters). Action Review shows it after `outside agent (sign-in <first 8 characters of the id>)` in **Asked by**; Genesis reduces it to visible one-line text. If omitted or blank-looking, it shows only that outside-agent sign-in label (`unknown` if the id is unavailable). It changes attribution only: authorization still uses the authenticated caller. |

<Note>
  `POST /api/tool_call` applies scoped input-safety checks before execution.
  Normal dbt/Jinja template syntax is allowed for `dbt_action` `query` and
  `sql_content`, and for `git_action` content fields when you are writing dbt
  project files under `models/`, `macros/`, `tests/`, `snapshots/`, or
  `analyses/` with `.sql`, `.yml`, or `.yaml` extensions.

  When calling `apps_action`, raw HTML/CSS/JS/SVG content is allowed only for
  `WRITE_FILE` or `WRITE_FILES` destinations under `templates/`, `static/`, or
  `assets/` ending in `.html`, `.htm`, `.css`, `.js`, `.mjs`, or `.svg`.

  For most string arguments, Genesis trims leading and trailing whitespace
  during sanitization. Arguments whose value is written to a file verbatim, or
  matched against file content verbatim, are preserved byte-for-byte instead:

  * `file`: `content`, `append_content`, `insert_content`, `old_string`,
    `new_string`
  * `git_action`: `content`
  * `apps_action`: `content`

  Use these paths when round-tripping file bodies — for example preserving a
  trailing newline on a write, or indentation in a search-and-replace. Other
  arguments on the same call (such as `action`, `repo_id`, or `file_path`) are
  still trimmed.

  The larger 2 097 152-character limit applies to `file` `content`,
  `append_content`, `insert_content`, `old_string`, and `new_string`, and to
  `apps_action` `content`. Other string arguments, including `git_action`
  `content`, retain the 50 000-character limit.

  After the caller is resolved and authorized, Genesis scans string arguments
  for disallowed HTML and template content before dispatching the tool.
</Note>

Malformed calls and content-scan refusals are returned as an HTTP `200`
refusal rather than a transport error, so MCP clients can read the tool result.
Genesis does not dispatch the tool. The outer response has `success: false`; its
nested `response` has `success: false`, `error_code: "invalid_request"`, an
`invalid_arguments` array with each failing argument and its problem, and a
`next_call` instruction. Request-validation refusals cover missing, unexpected,
or incorrectly typed arguments; missing required parameters are identified as
missing rather than as values that failed validation. Use
`/api/tool_call/schema` to check the accepted arguments.

Unowned sandbox runs have an additional execution contract. For these runs,
`POST /api/tool_call` returns HTTP `200` with `success: false` and does not call
the requested tool when it would perform **ticket mutation**, **run dispatch**,
or **remote publication and parent-side repository state**. The nested
`response` includes the `genesis-coding-contract-refusal` marker, a
`refusal.kind` of `"forbidden"`, and a `next` object explaining that the
refusal is final. This applies after the tool name is resolved and before any
durable thread is created. Read operations remain available, including the
permitted read actions for tools such as `linear_connector` and
`autodev_run_action`.

#### Response body

```json theme={null}
{
  "success": true,        // mirrors the tool's own success when present, else true
  "response": { ... }     // raw tool return value
}
```

The `response` field is whatever the tool returned. Most Genesis tools
return a dict whose first key is `Success` (capital `S` — that is the
tool-level success flag); the HTTP-level `success` mirrors it when
present.

Tool results are operation-specific. For the sign-in-dependent result and approval flow
of `prover_tools.request_evidence`, see its
[tool reference](/docs/Genbot-Toolkit/Additional-Tool-Groups#request_evidence).

#### Common status codes

| Status | When |
| - | - |
| `200` | Request was processed. Inspect `success` and `response` for the tool-level outcome or a tool safety rejection. |
| `400` | Invalid argument key or value (unsafe characters, too long, etc.). |
| `401` | Missing or invalid PAT / credentials. |
| `403` | Caller is missing the permission the tool needs. For multiplexed tools such as `lineage_tools_lineage_action`, some individual `action` values (for example `SET_ESTATE_TEMPLATE`) require an additional permission beyond the tool group gate. |
| `404` | `func` name not in the registry. Use `GET /api/tool_call/list`. |
| `500` | Tool raised an unhandled exception. |
| `503` | The requested tool's MCP service is unavailable. The error body includes `code` and `service`; `code: "service_starting"` means the service is still starting and can be retried in a few seconds, while `code: "service_unavailable"` means the service is not running on this server. |

<Note>
  When a dbt safety guard blocks a request or suppresses an unsafe dbt result,
  the endpoint still returns HTTP `200` with `success: false`. The nested
  `response` includes `dbt_safety_rejected: true` and either
  `preflight_rejected: true` or `postflight_rejected: true`. Every dbt failure
  also names its class: `error_class: "policy_block"` for a guard rejection —
  with `block_reason_code` (for example `static_output_pattern`,
  `judge_denied`, `judge_unavailable`) and a human-readable
  `policy_block_reason` — versus `error_class: "tool_error"` for a genuine dbt
  or dispatch failure. A parsed judge denial keeps
  `block_reason_code: "judge_denied"` and adds `judge_reason_code`. Recognized
  values are `secret_exposure`, `environment_access`, `host_details`,
  `file_or_process_access`, `protected_content`, `sandbox_bypass`, and
  `other_policy`; a legacy, missing, unknown, malformed, duplicate, or
  conflicting category becomes `unspecified` with the existing generic policy
  summary. The judge is instructed to return exactly `ALLOW` or
  `DENY: <reason_code>`, without a free-form rationale. Category extraction
  tolerates letter case, surrounding whitespace, and a sole code fence already
  accepted by the verdict parser; inline decoration and extra content remain
  generic. Public reason text is selected by Genesis from fixed summaries:
  neither the raw judge response nor the rejected input is reflected to the
  caller.

  For request-side `log()`, the judge is instructed to allow only literal
  message arguments and any supplied `info` argument must be a literal Boolean;
  the message content can contain no secret, credential, environment, host,
  account, or absolute-path data.
  Dynamic expressions, concatenations, variables, macros, attributes, and
  sensitive literal content remain deny cases. Projects on the
  `dbt.safety.trusted_projects` allowlist (see
  [Advanced Settings — dbt safety settings](/docs/Setup/Advanced-Settings#dbt-safety-settings))
  skip those static and LLM judges; host-path and secret redaction in
  `dbt_action` always still runs. Trusted projects still do not inherit ambient
  server variables; `dbt.safety.trusted_env_vars` is the only way to provide
  explicit non-secret `env_var(...)` values.
</Note>

## What `tool_call` does *not* do

`tool_call` is intentionally a thin wrapper. Compared to a tool call made
inside a real bot run, it skips:

* **Built-in / context tools** that depend on a live thread or chat
  context (tools that need `bot_id` / `thread_id` injection).
  `tool_call` injects the authenticated user as `bot_id` and a
  synthetic `api_call_<id>` as `thread_id`. When `agent_id` is supplied,
  tools that record the acting agent use that validated persona for attribution.
* **Streaming** — there is no incremental output channel. You get one
  JSON response.
* **Recovery and retries** that the bot loop normally provides.
* **Cost accounting** in chat budgets.

If a tool reports it needs thread or bot context, run it through an agent
via the [chat API](/docs/Genesis-API/Chat-Endpoints) instead.

## Examples

### List document indexes

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{"func": "_document_index", "func_args": {"action": "LIST_INDICES"}}' \
  "$GENESIS_URL/api/tool_call"
```

### Search the `wiki` index

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "_document_index",
        "func_args": {
          "action": "SEARCH",
          "index_name": "wiki",
          "query": "deployment runbook",
          "top_n": 5
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

### Add a file by `file_id` to an index

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "_document_index",
        "func_args": {
          "action": "ADD_DOCUMENTS",
          "index_name": "docs",
          "file_id": "123e4567-e89b-12d3-a456-426614174000",
          "conflict_policy": "overwrite_on_conflict"
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

### Ask the document indexes (RAG-style)

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "_document_index",
        "func_args": {
          "action": "ASK",
          "query": "How do we rotate the LiteLLM proxy key?",
          "top_n": 3
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

### Bulk-delete by metadata

Useful for sweeping out all entries from a single source:

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "_document_index",
        "func_args": {
          "action": "DELETE_BY_METADATA",
          "index_name": "snowflake_metadata",
          "metadata": {"qualified_name": "snowflake://DB/SCHEMA/TABLE"}
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

### Digital Twin lineage actions

For Digital Twin scan control and estate layout, call
`lineage_tools_lineage_action` through `POST /api/tool_call`.

| Action | Additional args | Notes |
| - | - | - |
| `GET_SCAN_PROGRESS` | none | Returns `progress[]` entries with `source_id`, `source_name`, `continuous`, `cycles_completed`, `total_files_discovered`, `total_files_scanned`, `queue_remaining`, `current_cycle_progress_pct`, `is_scanning`, and `is_queued`. |
| `SET_SCAN_CONTINUOUS` | `source_id` (`string`), `properties.enabled` (`boolean`, default `true`) | Toggles auto-cycle for a source's active scan. |
| `SCAN_SOURCE` | `source_id` (`string`), optional `properties.continuous` (`boolean`, default `true`) | Queues a per-source scan and can disable follow-on cycles at enqueue time. |
| `SET_USAGE_BUDGET` | `properties.token_budget` (`integer` or `null`), optional `properties.budget_window_hours` (`integer`) | Sets or clears the global Context Graph token cap. `null` removes the cap; `0` blocks background work. An omitted window keeps the existing window, or uses the first-ever default of `720` hours. Requires `access_config_panel`. |
| `SET_ESTATE_TEMPLATE` | `template` (`string[]`) | Validate, persist, and regenerate the shared **Data Estate** page (`wiki://estate`). Requires `access_config_panel`. `template` must be a non-empty list of unique block type names drawn from `overview`, `areas`, and `systems` (default order: `["overview", "areas", "systems"]`). On success, `response.page` includes the regenerated page plus `template` and `available_block_types`. On validation failure, `response.success` is `false`, `response.error` describes the problem, and `response.available_block_types` lists valid block types. |

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "lineage_tools_lineage_action",
        "func_args": {
          "action": "GET_SCAN_PROGRESS"
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

```json theme={null}
{
  "success": true,
  "response": {
    "success": true,
    "progress": [
      {
        "source_id": "repo_customer-onboarding",
        "source_name": "customer-onboarding",
        "continuous": true,
        "cycles_completed": 1,
        "total_files_discovered": 842,
        "total_files_scanned": 520,
        "queue_remaining": 322,
        "current_cycle_progress_pct": 61,
        "is_scanning": true,
        "is_queued": false
      }
    ]
  }
}
```

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "lineage_tools_lineage_action",
        "func_args": {
          "action": "SET_SCAN_CONTINUOUS",
          "source_id": "repo_customer-onboarding",
          "properties": {
            "enabled": false
          }
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

### Update the Data Estate section template

```bash theme={null}
curl -sS -X POST "${AUTH[@]}" -H 'Content-Type: application/json' \
  -d '{
        "func": "lineage_tools_lineage_action",
        "func_args": {
          "action": "SET_ESTATE_TEMPLATE",
          "template": ["areas", "systems", "overview"]
        }
      }' \
  "$GENESIS_URL/api/tool_call"
```

```json theme={null}
{
  "success": true,
  "response": {
    "success": true,
    "page": {
      "qualified_name": "wiki://estate",
      "title": "Data Estate",
      "body_markdown": "...",
      "template": ["areas", "systems", "overview"],
      "available_block_types": ["overview", "areas", "systems"]
    }
  }
}
```

### Python wrapper

```python theme={null}
import os, requests

def call_tool(func: str, **kwargs) -> dict:
    resp = requests.post(
        f"{os.environ['GENESIS_URL'].rstrip('/')}/api/tool_call",
        headers={"Authorization": f"Bearer {os.environ['GENESIS_PAT']}"},
        json={"func": func, "func_args": kwargs},
        timeout=120,
    )
    resp.raise_for_status()
    body = resp.json()
    if not body.get("success", True):
        raise RuntimeError(body)
    return body["response"]


hits = call_tool(
    "_document_index",
    action="SEARCH",
    index_name="wiki",
    query="kubernetes deployment",
    top_n=5,
)
for hit in hits.get("results", []):
    print(hit.get("source"), hit.get("score"))
```

## Discovering tool names and arguments

Most Genesis tools are documented in the
[Genesis Data Agent's Toolkit](/docs/Genbot-Toolkit/Tools-Overview)
section. Each toolkit page lists the tool function names along with the
arguments the agents use, which are the same names and arguments you pass
in `func` and `func_args`.

If you already know the tool name and need a machine-readable argument schema,
call `POST /api/tool_call/schema`. That endpoint returns the same LLM-facing
schema Genesis exposes internally, along with the tool groups the function
belongs to.

A few popular starting points:

* [Document Index Tools](/docs/Genbot-Toolkit/Document-Index-Tools)
* [Knowledge Base Admin Tools](/docs/Genbot-Toolkit/Knowledge-Base-Admin-Tools)
* [File Manager Tools](/docs/Genbot-Toolkit/File-Manager-Tools)
* [Snowflake Tools](/docs/Genbot-Toolkit/Snowflake-Tools)
* [Process Scheduler Tools](/docs/Genbot-Toolkit/Process-Scheduler-Tools)
* [Project Manager Tools](/docs/Genbot-Toolkit/Project-Manager-Tools)

For the all-purpose Document Index tool covered above, the action names
are upper-snake-case strings:

```
ADD_DOCUMENTS, ADD_TEXT, CREATE_INDEX, LIST_INDICES, DELETE_INDEX,
LIST_DOCUMENTS, SEARCH, RENAME_INDEX, ASK, DELETE_DOCUMENT,
DELETE_BY_METADATA, GET_DOCUMENT, RENAME_DOCUMENT, GET_INDICES_STATS
```

The
[Document Index Endpoints](/docs/Genesis-API/Document-Index-Endpoints)
page covers the same actions through typed REST endpoints, which can be
more convenient for typed clients.

## Related pages

* [Personal Access Tokens](/docs/User-Guide/Personal-Access-Tokens)
* [Chat with agents over REST](/docs/Genesis-API/Chat-Endpoints)
* [Document Index Endpoints](/docs/Genesis-API/Document-Index-Endpoints)
* [Genesis Data Agent's Toolkit overview](/docs/Genbot-Toolkit/Tools-Overview)
