> For the complete documentation index, see [llms.txt](https://docs.erathos.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.erathos.com/platform/mcp/available-tools.md).

# Available Tools

Reference for every tool the Erathos MCP server exposes.

This page lists every tool the Erathos MCP server exposes to an AI agent, grouped by what they operate on. Every tool is scoped to one workspace unless noted otherwise — get a valid `workspace_id` from **`list_workspaces`** first, then pass it explicitly to any other tool; nothing defaults to "the current workspace." Each entry below is collapsed by default — click a tool's name to expand its full parameters and return fields.

A few conventions hold across every tool:

* **IDs are consistent.** A `job_id`, `connection_id`, `workspace_id` and so on returned by one tool means the same record wherever another tool takes a parameter of that name.
* **List tools paginate the same way.** `limit`/`offset` page through `results`; keep raising `offset` by `limit` while `meta.has_more` is true.
* **`meta.platform_url`** is a best-effort link to the same result in the platform, and **`meta.hint`** carries advice about the result when there is any (e.g. why a list came back empty).
* **Timestamps are in your own timezone.** Every datetime a tool returns carries your UTC offset (the `timezone` `get_me` returns), as the platform shows them. Datetimes you pass in may use any offset.

## Workspace & Identity

<details>

<summary>list_workspaces</summary>

List the workspaces the authenticated user belongs to, with the `workspace_id` every other tool takes. Membership is checked again on every call.

**Example prompts**

* "Which workspaces do I have access to?"
* "What's the workspace ID for Acme Corp?"

**Parameters**

* No parameters.

**Returns**

* **`results`** (list) — one entry per workspace:
  * **`workspace_id`** (string)
  * **`name`** (string)

</details>

<details>

<summary>get_me</summary>

Return the authenticated user's own identity. Useful for tools that act "as me," and for resolving the timezone metric buckets are shown in.

**Example prompts**

* "Who am I logged in as?"
* "What timezone is my account set to?"

**Parameters**

* No parameters.

**Returns**

* **`user_id`** (string)
* **`email`** (string)
* **`timezone`** (string, nullable) — the timezone every tool shows timestamps in, and the one `get_workspace_metrics` buckets times in.
* **`meta.platform_url`** — link to the user's profile settings page.

</details>

## Connections & Destinations

<details>

<summary>list_connections</summary>

List the source connections in a workspace, with where each one stands in its setup.

**Example prompts**

* "What connections do I have set up in this workspace?"
* "Which of my connections still need setting up?"
* "Do any of my connections have failing jobs right now?"

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, optional) — filter to one connection, e.g. to check on a setup while it validates.
* **`is_api`** (boolean, optional) — filter to API-type connectors only.
* **`is_database`** (boolean, optional) — filter to database-type connectors only.

**Returns**

* **`results`** (list) — one entry per connection:
  * **`connection_id`** (string)
  * **`name`** (string)
  * **`source_connector_id`** (string) — the connector *type* (e.g. Postgres), as opposed to this one connection instance.
  * **`source_connector_name`** (string)
  * **`pipeline_id`** (string, nullable)
  * **`active_jobs`** (integer) — this connection's count of currently-active jobs.
  * **`failed_jobs`** (integer) — how many of those had their last run fail.
  * **`show_warning`** (boolean) — `failed_jobs > 0`.
  * **`has_valid_credentials`** (boolean, nullable) — null until the credentials are first checked, then whether the last check passed.
  * **`last_successful_connection_at`** (datetime, nullable) — null if a check has never passed.
  * **`setup_status`** (string) — the connection's next setup step:
    * `awaiting_credentials` — someone has to enter or fix its credentials in the platform (or save them again, if its settings' options were never loaded).
    * `lost_authentication` — credentials that worked before failed their last check; someone has to fix them in the platform, and its jobs can't run meanwhile.
    * `validating` — a background check is running (e.g. reading the schema); just wait.
    * `needs_settings` — choose its settings with `configure_connection`.
    * `no_options_available` — its credentials work but give access to no options for a setting it needs (e.g. no account visible to them); someone has to sign in with credentials that have access, then save them again.
    * `schema_unavailable` — its tables/endpoints, or its settings' options, couldn't be read, or none were found. The error from `configure_connection`/`configure_jobs` says what to check.
    * `ready_for_jobs` — create its jobs with `configure_jobs`.
    * `no_destination` — its jobs are set up, but the workspace has no working destination to load into (see `get_destination`).
    * `configured` — set up and able to run.
  * **`running_validation`** (string, nullable) — what the background check currently running is doing, in plain words (e.g. "checking its credentials").
* **`meta.platform_url`** — link to the workspace's connections page.

</details>

<details>

<summary>list_source_connectors</summary>

List the connector types (e.g. "HubSpot", "Postgres") a `source_connector_id` can refer to — the discovery entrypoint for that id across `list_jobs`, `list_runs`, `list_connections`, and `list_endpoints`.

**Example prompts**

* "What connector types are available for me to connect as a source?"
* "Is there a HubSpot connector I can use?"

**Parameters**

* **`workspace_id`** (string, required)
* **`connector_id`** (string, optional) — filter to one connector type, e.g. to look up a `source_connector_id` seen in another tool.
* **`name`** (string, optional) — substring search to resolve a connector by name (e.g. `name='hubspot'`).
* **`is_api`** (boolean, optional)
* **`is_database`** (boolean, optional)
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per connector type:
  * **`connector_id`** (string)
  * **`name`** (string)
  * **`description`** (string, nullable)
  * **`is_api`** (boolean)
  * **`is_database`** (boolean)
  * **`is_custom_connector`** (boolean) — true for a connector type private to this workspace, false for one available to every workspace.
  * **`release_stage`** (string, nullable) — the connector's own maturity: `SOON`, `BETA`, or `STABLE`.
  * **`docs_url`** (string, nullable)
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.
* **`meta.platform_url`** — link to where a human would pick one of these to start a new connection.

</details>

<details>

<summary>get_destination</summary>

Get the workspace's destination — the single data warehouse its pipelines load into. Errors if none has been created yet.

**Example prompts**

* "What's our workspace's destination warehouse?"
* "Are the destination's credentials still valid?"

**Parameters**

* **`workspace_id`** (string, required)

**Returns**

* **`destination_id`** (string)
* **`name`** (string)
* **`connector_id`** (string)
* **`connector_name`** (string)
* **`has_valid_credentials`** (boolean, nullable) — null until the first connection attempt, then true/false for whether the last attempt's credentials worked.
* **`has_valid_schema`** (boolean, nullable) — same idea, but for the destination's schema/database setup. Both must be true for the destination to actually be usable.
* **`last_successful_connection_at`** (datetime, nullable)
* **`meta.platform_url`** — link to the destination in the platform.

</details>

<details>

<summary>list_destination_connectors</summary>

List the connector types (e.g. "Snowflake", "BigQuery") that can serve as a workspace's destination/warehouse.

**Example prompts**

* "What warehouse types can I use as a destination?"
* "Is BigQuery available as a destination option?"

**Parameters**

* **`workspace_id`** (string, required)
* **`connector_id`** (string, optional) — filter to one connector type.
* **`name`** (string, optional) — substring search (e.g. `name='snowflake'`).
* **`is_api`** (boolean, optional)
* **`is_database`** (boolean, optional)
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — same shape as `list_source_connectors`'s entries: **`connector_id`**, **`name`**, **`description`** (nullable), **`is_api`**, **`is_database`**, **`is_custom_connector`**, **`release_stage`** (nullable), **`docs_url`** (nullable).
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.
* **`meta.platform_url`** — link to the workspace's destination settings page.

</details>

<details>

<summary>create_connection</summary>

Create a new source connection in a workspace. This creates the connection shell only — the tool never accepts credentials as a parameter, since a credential passed as a tool argument would be visible to the agent and to the conversation. The human finishes the setup by entering credentials in the platform, at the link returned in `meta.platform_url`. Until they do, the connection has no valid credentials, and `list_connections`' `setup_status` says when it's ready for the next step.

**Example prompts**

* "Create a new HubSpot connection called 'HubSpot - Marketing'."
* "Set up a Postgres connection for our production database."

**Parameters**

* **`workspace_id`** (string, required)
* **`source_connector_id`** (string, required) — the connector type, from `list_source_connectors`.
* **`name`** (string, required)

**Returns**

* **`connection_id`** (string)
* **`name`** (string)
* **`source_connector_id`** (string)
* **`source_connector_name`** (string)
* **`meta.platform_url`** — link to the platform page where the human enters the connection's credentials.
* **`meta.hint`** — if the credentials will be entered by someone without an Erathos account, use `create_connection_secure_link` instead of sharing `platform_url`.

</details>

<details>

<summary>create_destination</summary>

Create the workspace's destination. A workspace can have at most one, so this errors if it already has one (`get_destination` shows it; deleting it is only possible in the platform). Like `create_connection`, this creates the destination shell only and never accepts credentials; the human finishes the setup at `meta.platform_url`. Until they do, the destination has no valid credentials (`has_valid_credentials` is null in `get_destination`) and no pipeline can load into it.

**Example prompts**

* "Set up BigQuery as our destination warehouse."
* "Create a Snowflake destination called 'Analytics Warehouse'."

**Parameters**

* **`workspace_id`** (string, required)
* **`destination_connector_id`** (string, required) — the connector type, from `list_destination_connectors`.
* **`name`** (string, required)

**Returns**

* **`destination_id`** (string)
* **`name`** (string)
* **`connector_id`** (string)
* **`connector_name`** (string)
* **`meta.platform_url`** — link to the workspace's destination settings page, where the human enters the credentials.
* **`meta.hint`** — if the credentials will be entered by someone without an Erathos account, use `create_connection_secure_link` (passing this `destination_id` as `connection_id`) instead of sharing `platform_url`.

</details>

<details>

<summary>create_connection_secure_link</summary>

Create a secure link that lets whoever holds it submit credentials for one connection or for the destination, without an Erathos account — for when the person who actually has the credentials (e.g. your company's DBA) isn't a platform user. Works for both credential-form connectors (username/password/API key) and OAuth-based ones; the recipient sees whichever the connector needs.

The link is single-use: it stops working as soon as credentials are submitted successfully, when it's revoked (see `revoke_connection_secure_link`), or when it expires.

**Example prompts**

* "Create a link I can send to our DBA so they can enter the Postgres credentials."
* "Generate a secure link for the Snowflake destination that's valid for 2 hours."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, required) — a connection from `list_connections`, or the destination's `destination_id` from `get_destination`.
* **`ttl_hours`** (integer, optional, default 24) — how long the link stays valid. Use a shorter value if it will be sent through a less trusted channel, or a longer one if the recipient may not act on it right away.

**Returns**

* **`share_link_id`** (string) — an 8-character id; use it with `list_secure_links` and `revoke_connection_secure_link`.
* **`key`** (string)
* **`expires_at`** (datetime, nullable)
* **`meta.platform_url`** — the link to send to whoever will submit the credentials (not a link for you).

{% hint style="warning" %}
The link is shown only once, in this response. It isn't stored in recoverable form, so `list_secure_links` can't return it later. If it's lost before the recipient uses it, revoke it and create a new one.
{% endhint %}

</details>

<details>

<summary>list_secure_links</summary>

List the secure links created in a workspace, across every connection and the destination, or for just one of them. Use it to find a `share_link_id` to revoke, or to check whether a link has been used yet (and from which IP). The raw link itself is never returned.

**Example prompts**

* "Has our DBA used the credentials link I sent yet?"
* "Show me all the secure links still open in this workspace."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, optional) — limit the list to one connection or the destination.
* **`share_link_id`** (string, optional) — filter to one link, e.g. to check whether a link just sent has been used.

**Returns**

* **`results`** (list) — one entry per link, newest first:
  * **`share_link_id`** (string)
  * **`connection_id`** (string)
  * **`connection_name`** (string)
  * **`created_at`** (datetime)
  * **`expires_at`** (datetime, nullable)
  * **`consumed_at`** (datetime, nullable) — when credentials were submitted through the link; null if it hasn't been used.
  * **`consumed_from_ip`** (string, nullable)
  * **`revoked`** (boolean)

</details>

<details>

<summary>revoke_connection_secure_link</summary>

Revoke a secure link before it's used, so it stops working immediately. Revoking can't be undone — create a new link if one is needed again.

**Example prompts**

* "Revoke the credentials link I sent yesterday."
* "Cancel every unused secure link for the Postgres connection."

**Parameters**

* **`workspace_id`** (string, required)
* **`share_link_id`** (string, required) — from `create_connection_secure_link` or `list_secure_links`.

**Returns**

* **`share_link_id`** (string)
* **`revoked`** (boolean)

</details>

## Connection Setup

Setting up a connection goes: `create_connection` → someone enters the credentials in the platform → `configure_connection`, only if the connection has settings to choose → `configure_jobs` → `trigger_run`. `list_connections`' `setup_status` says which step a connection is at.

The agent doesn't need to ask users about sync types or cursors. It asks what they need instead — how fresh the data must be, and whether they want each row's latest version or its history of changes — and passes on the plain-language explanations the tools return.

<details>

<summary>list_connection_settings</summary>

List a connection's own settings (e.g. which Google Ads ad account it reads from, or which Google Sheets files) and, for choice-type settings, the options to pick from.

**Example prompts**

* "Which ad accounts can this Google Ads connection read from?"
* "What settings does the new connection still need?"

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, required)
* **`setting_id`** (string, optional) — narrow the response to one setting.
* **`name`** (string, optional) — search the options by value.
* **`limit`** (integer, optional) / **`offset`** (integer, optional) — page through each setting's options separately.

**Returns**

* **`connection_id`** (string)
* **`is_configured`** (boolean) — whether the settings have been chosen.
* **`running_validation`** (string, nullable) — what a background check still running on the connection is doing, if any (e.g. "loading the options for its settings").
* **`settings`** (list) — one entry per setting:
  * **`setting_id`** (string)
  * **`name`** (string)
  * **`description`** (string, nullable)
  * **`example`** (string, nullable)
  * **`data_type`** (string)
  * **`accept_multiple_values`** (boolean)
  * **`is_required`** (boolean)
  * **`edit_mode`** (string) — what can still change once chosen: `locked` (never — a different value means a new connection) or `append` (it can only gain values, e.g. more files).
  * **`current_values`** (list of strings) — the values chosen so far.
  * **`options`** (list, nullable) — for a choice-type setting, each with **`option_id`** and **`value`**.
  * **`options_loaded`** (boolean, nullable) — `false` means the options haven't been loaded yet (the connection needs working credentials first), not that there are none. `true` with no options means the source offered none to these credentials; `meta.hint` says what to do.
  * **`options_has_more`** (boolean, nullable) — whether this setting has more options past `limit`.

</details>

<details>

<summary>configure_connection</summary>

Choose a connection's settings, from `list_connection_settings`. The first call chooses them and creates the connection's pipeline (named by `name`). After that, each setting's `edit_mode` decides what can change: a `locked` setting never can, and an `append` setting only gains values. A setting left out stays as it is, and repeating a call changes nothing. The connection's credentials must be validated first.

{% hint style="warning" %}
Most settings are locked once chosen — a different value later means creating a new connection. Confirm them before the first call, even when there's only one option.
{% endhint %}

**Example prompts**

* "Use the 'Acme - Brazil' ad account for this Google Ads connection."
* "Add these two spreadsheets to the Google Sheets connection."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, required)
* **`settings`** (list, optional) — one entry per setting to set: **`setting_id`** plus **`values`** (for a choice-type setting, its `option_id`s, or an option's exact text when no other option shares it). For an `append` setting, pass just the new values: saved ones left out are kept, and ones already saved are skipped.
* **`name`** (string, optional) — the pipeline's name, on the first call.

**Returns**

* **`connection_id`** (string)
* **`pipeline_name`** (string)
* **`created`** (boolean) — whether this call created the pipeline.
* **`settings`** (list) — every setting as saved now, each with **`setting_id`**, **`name`**, **`values`**, **`edit_mode`**.
* **`added`** (list) — what this call added, same shape.
* **`background_work_queued`** (boolean) — the platform queued a refresh for the new values (e.g. reading new files as tables); `configure_jobs` waits for it.

</details>

<details>

<summary>configure_jobs</summary>

Create or edit a connection's jobs — which tables (database) or endpoints (API) sync, and how — applying one set of settings to a whole selection. Call it again for other groups; later calls refine earlier ones.

It's a **dry run by default**: it returns a preview of exactly what would happen, to show to the user before calling again with `dry_run=false`. The preview goes through the same save the platform's manage-jobs page uses, and is then rolled back.

**Example prompts**

* "Sync every table in the 'public' and 'sales' schemas, daily, keeping just the latest version of each row."
* "Sync the 'deals' endpoint every hour."
* "Stop syncing the 'email' column on the customers table."

**Parameters**

*What to configure* — combined with AND, at least one:

* **`table_ids`** / **`endpoint_ids`** (list of strings, optional) — from `list_tables`/`list_endpoints`.
* **`job_ids`** (list of strings, optional) — existing jobs, from `list_jobs`.
* **`schema_names`** (list, optional) — whole schemas; `null` stands for tables outside any schema, as `list_table_schemas` reports them.
* **`name_contains`** (string, optional)
* **`select_all`** (boolean, optional, default false)
* **`scope`** (string, optional, default `all`) — `existing_only` (only tables/endpoints that already have a job) or `new_only` (only those that don't).

*How* — every setting is optional. On an existing job, leaving one out keeps the current value; a new job gets the recommendation:

* **`sync_frequency_id`** (string, optional) — from `list_sync_frequencies`. New jobs default to daily (or the plan's fastest allowed frequency, if daily isn't).
* **`sync_time`** (string, optional) — `HH:MM` in the user's own timezone. New jobs default to `03:00`.
* **`sync_type`** (string, optional) — `recommended` (the default for new jobs: incremental where the table/endpoint supports it, full elsewhere), `full`, `incremental_append`, `incremental_overwrite`, or `incremental_versioned`.
* **`incremental_mode`** (string, optional, default `overwrite`) — the incremental flavor `recommended` uses: `overwrite` keeps each row's latest version, `versioned` keeps a history of every change, `append` suits tables that only ever get new rows.
* **`cursor_column`** (string or list of strings, optional, tables only) — the column(s) to track changes by. Naming them replaces the cursors of existing jobs too, resetting their progress. `auto` picks a common "last updated" column, and only for jobs that don't have any cursor yet.
* **`cdc`** (boolean, optional) — on a database connection with CDC enabled, incremental tables sync through CDC by default (reading the database's change log, so they need a primary key but no cursor); `cdc=false` uses a cursor instead.
* **`on_incremental_unavailable`** (string, optional, default `skip`) — what happens to tables/endpoints that can't sync incrementally as asked: `skip` reports them in `needs_decision`/`full_only`, and `full` configures them as full refresh on the fallback schedule.
* **`fallback_sync_frequency_id`** / **`fallback_sync_time`** (string, optional) — that fallback schedule.
* **`weekly_run_day`** (string, optional) — the day jobs on the weekly frequency run (default Sunday).
* **`full_reload`** (string, optional) — a periodic full reload of the history, for incremental jobs syncing daily or more often: `never` (the default for new jobs), `every_day`, or a weekday.
* **`columns`** (object, optional) — changes to the column selection, by name: **`exclude`** / **`include`**, **`anonymize`** / **`deanonymize`**.
* **`enabled`** (boolean, optional)
* **`dry_run`** (boolean, optional, default true)

**Returns**

* **`dry_run`** (boolean)
* **`pipeline`** (string) — `existing`, `created`, or `will_create` (a connection without settings gets its pipeline on the first save).
* **`summary`** (list) — the resulting jobs, in groups that share the same settings:
  * **`sync_type`** (string), **`cdc`** (boolean), **`enabled`** (boolean), **`is_new`** (boolean)
  * **`cursor_columns`** (list of strings) — what changes are tracked by; empty for a full refresh, a CDC job, or a child endpoint that goes through its parent.
  * **`updated_through`** (string, nullable) — for a child endpoint without a cursor of its own, the parent endpoint whose filter decides what it fetches.
  * **`sync_frequency`** (string, nullable) and **`sync_time`** (string, nullable) — the frequency and the local time it runs at.
  * **`schedule`** (string) — when it runs, in plain words (e.g. "Every 6h, at 03:00, 09:00, 15:00 and 21:00").
  * **`count`** (integer) and **`names`** (list of strings) — how many tables/endpoints the group has, and which (e.g. `public.orders`).
  * **`explanation`** (string) — what this sync does, in plain language.
* **`unchanged`** (integer) — selected jobs already as requested, which are left untouched.
* **`fell_back_to_full`** (object, nullable) — jobs set to a full refresh because they can't sync incrementally: **`count`**, **`reason_counts`**, **`explanation`**.
* **`needs_decision`** (list) — tables left out because they have no column matching the cursor asked for (or, with `auto`, no common "last updated" column) but could use another one. Each has **`entity_id`**, **`name`**, **`reason`**, **`options`** (`full` plus the columns it could track changes by) and **`explanation`**. At most 50 are listed; **`needs_decision_total`** (integer) counts them all, and `list_table_columns` with `cursor_candidates_only=true` lists every usable column.
* **`full_only`** (object, nullable) — tables/endpoints left out because they can only sync in full: **`count`**, **`reason_counts`**, **`explanation`**. Reasons are `no_primary_key`, `no_cursor`, `parent_not_eligible`, or `child_not_eligible`.
* **`impacts`** (list) — side effects on existing jobs, each with **`kind`**, **`count`** and **`explanation`** (e.g. a changed cursor reloads the full history; jobs outside the selection that follow a parent in it).
* **`added_ancestors`** (list of strings) — parent endpoints added because a selected child needs them.
* **`overridden_by_parent`** (integer) — child jobs whose schedule follows their parent instead of the one asked for.
* **`columns_not_present_on`** (integer) — jobs where a column named in `columns` doesn't exist. That's not an error, since `columns` applies to the whole selection.
* **`errors`** (list) — per-job errors, each with **`entity_id`**, **`name`**, **`messages`**.
* **`plan`** — **`active_jobs_after`**, **`jobs_limit`** (nullable) and **`over_limit`**. Applying a change over the plan's job limit fails as a whole.
* **`timezone`** (string) — the user's timezone, which `sync_time` is in.
* **`removed_from_source`** (list) — the connection's jobs whose table/endpoint no longer exists at the source, each with **`job_id`**, **`name`**, **`enabled`**. They can't be configured, and stay active so their failing runs alert the user; `disable_job` stops them.
* **`meta.platform_url`** — link to the connection's jobs in the platform.

</details>

## Jobs & Runs

<details>

<summary>list_jobs</summary>

List the jobs (one per synced table/endpoint) in a workspace.

**Example prompts**

* "What jobs are set up in this workspace?"
* "Show me the jobs that are currently inactive."
* "Which jobs haven't synced successfully in the last week?"

**Parameters**

* **`workspace_id`** (string, required)
* **`job_id`** (string, optional) — filter to one specific job.
* **`connection_id`** (string, optional) — filter by one specific connection instance.
* **`source_connector_id`** (string, optional) — filter by connector *type* (e.g. all jobs on any Postgres connection).
* **`api_endpoint_id`** (string, optional)
* **`company_table_id`** (string, optional) — a job has exactly one of `api_endpoint_id`/`company_table_id` set, never both.
* **`is_active`** (boolean, optional)
* **`sync_type`** (string, optional) — one of the values `list_sync_types` returns (e.g. `incremental_overwrite`).
* **`sync_frequency_id`** (string, optional) — see `list_sync_frequencies` for valid values.
* **`trigger_type`** (string, optional) — one of `MANUAL`, `SCHEDULE`, `PARENT`.
* **`next_execution_before`** (datetime, optional)
* **`freshness_before`** (datetime, optional) — jobs that haven't succeeded since this time (also matches jobs that have never succeeded). More expensive than the other filters — only pass it when you actually need to filter by staleness.
* **`latest_error_after`** (datetime, optional) — jobs that have failed since this time. Also more expensive; same caveat as `freshness_before`.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per job:
  * **`job_id`** (string)
  * **`name`** (string, nullable)
  * **`is_active`** (boolean)
  * **`connection_id`** (string, nullable)
  * **`connection_name`** (string, nullable)
  * **`source_connector_id`** (string, nullable)
  * **`source_connector_name`** (string, nullable)
  * **`api_endpoint_id`** (string, nullable)
  * **`company_table_id`** (string, nullable)
  * **`sync_type`** (string, nullable) — the same values `list_sync_types` returns.
  * **`cdc`** (boolean) — whether it reads the database's change log instead of cursors.
  * **`source_removed`** (boolean) — its table/endpoint no longer exists at the source. It stays active so its failing runs alert the user; `disable_job` stops it.
  * **`sync_frequency_id`** (string, nullable)
  * **`sync_frequency`** (string, nullable) — its platform-facing description.
  * **`sync_time`** (string, nullable) — `HH:MM` in the user's timezone; null for a manually-run job.
  * **`schedule`** (string, nullable) — when the job runs, in plain words (e.g. "Every 6h, at 01:30, 07:30, 13:30 and 19:30, with a full reload on Sundays at 01:30").
  * **`trigger_type`** (string) — `MANUAL`, `SCHEDULE`, or `PARENT`.
  * **`next_execution_at`** (datetime, nullable)
  * **`freshness`** (datetime, nullable) — when the job's data was last successfully extracted; null if never.
  * **`freshness_run_id`** (string, nullable) — that run, for `get_run`.
  * **`latest_error`** (datetime, nullable) — when the job last failed.
  * **`latest_error_run_id`** (string, nullable) — that run, for `get_run`.
  * **`cursors`** (list) — the column(s)/field(s) its incremental sync tracks changes by (one or several), each with its current bookmark; empty for a full-refresh job or a CDC job. Each entry has **`field_id`** (nullable), **`field_name`**, **`field_type`**, **`value`** (nullable, as text).
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.
* **`meta.platform_url`** — link to the jobs in the platform.

</details>

<details>

<summary>list_runs</summary>

List a workspace's job runs (executions), most recent first.

**Example prompts**

* "Show me the most recent runs for this workspace."
* "Which runs failed today?"
* "Show me all full-refresh runs for this connection."

**Parameters**

* **`workspace_id`** (string, required)
* **`job_id`** (string, optional)
* **`run_id`** (string, optional)
* **`connection_id`** (string, optional)
* **`source_connector_id`** (string, optional)
* **`parent_job_id`** (string, optional)
* **`parent_run_id`** (string, optional)
* **`status`** (string, optional) — one of `BLOCKED`, `WAITING`, `QUEUED`, `EXECUTING`, `FINISHED`, `FAILED`, `CANCELED`. The first four are still in flight; the last three are terminal.
* **`trigger_type`** (string, optional)
* **`is_full_refresh`** (boolean, optional) — whether the run did a full resync rather than an incremental one.
* **`scheduled_after`** (datetime, optional) — filters by when the run was scheduled, not when it started/finished. Clamped to the workspace's plan-based retention window if it asks further back than the plan allows (or is left out).
* **`scheduled_before`** (datetime, optional)
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per run:
  * **`run_id`** (string)
  * **`job_id`** (string, nullable)
  * **`connection_id`** (string, nullable)
  * **`connection_name`** (string, nullable)
  * **`source_connector_id`** (string, nullable)
  * **`source_connector_name`** (string, nullable)
  * **`source_connector_type`** (string, nullable) — `API` or `DATABASE`.
  * **`name`** (string, nullable)
  * **`parent_job_id`** (string, nullable)
  * **`parent_run_id`** (string, nullable)
  * **`status`** (string)
  * **`trigger_type`** (string)
  * **`triggered_by`** (string, nullable) — the triggering user's email; internal Erathos staff emails are masked to a generic support address when viewed from a customer workspace.
  * **`is_full_refresh`** (boolean)
  * **`scheduled_at`** (datetime)
  * **`started_at`** (datetime, nullable)
  * **`finished_at`** (datetime, nullable)
  * **`duration_seconds`** (integer, nullable)
  * **`requests`** (integer) — source API requests made.
  * **`retries`** (integer)
  * **`retry_backoff_seconds`** (number)
  * **`rows`** (integer)
  * **`errors`** (list) — this run's own error log entries (only meaningful for a FAILED run), each with a **`details`** (string).
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.
* **`meta.paywalled_since`** (datetime, nullable) — set when `scheduled_after` was clamped forward to the workspace's plan-based retention window.
* **`meta.platform_url`** — link to the workspace's runs page (pagination only; other filters aren't reflected in the link).

</details>

<details>

<summary>get_run</summary>

Get the full detail for one run — everything `list_runs` returns for it, plus data too heavy for a list row: a per-model metrics breakdown, every log entry (not just failures), the run's execution timeline, and its cursor bookmark snapshot.

**Example prompts**

* "Why did this run fail? Show me the full details."
* "Give me the complete log and timeline for that run."

**Parameters**

* **`workspace_id`** (string, required)
* **`run_id`** (string, required) — from `list_runs`, `trigger_run`, or a job's `freshness_run_id`/`latest_error_run_id`.

**Returns**

* **`run`** — the same shape as one `list_runs` result entry.
* **`metrics`** (list) — a breakdown per extracted model (the main one plus any nested sub-models, e.g. line items nested under an order — `list_runs`'s own requests/retries/rows only ever reflect the main model). Each entry has **`model_name`**, **`is_nested_model`** (boolean), **`rows`** (integer), **`invalid_datatype_rows`** (integer), **`extract_requests`** (integer), **`load_requests`** (integer), **`extract_retries`** (integer), **`load_retries`** (integer), **`extract_backoff_seconds`** (number), **`load_backoff_seconds`** (number).
* **`logs`** (list) — every log entry for this run, not just failures. If the run failed but has no logs of its own, its parent run's logs are returned instead (marked `is_parent_error`). Each entry has **`log_id`**, **`failed_at`** (datetime), **`failed_after_seconds`** (integer), **`is_parent_error`** (boolean), **`connector_stage`** (string, nullable), **`details`** (string, nullable), **`external_api_http_code`** (integer, nullable).
* **`timeline`** (list) — the run's execution stages in order (e.g. connecting, requesting, parsing, loading). Each entry has **`execution_type`**, **`trace_type_name`**, **`trace_type_description`**, **`table_name`** (nullable), **`started_at`** (nullable), **`finished_at`** (nullable), **`duration_seconds`** (nullable), **`trace_count`** (integer), **`has_errors`** (boolean).
* **`cursors`** (list) — this run's incremental-sync bookmark snapshot(s) as of when it ran (empty for a full-refresh run); same field name as `list_jobs`'s own `cursors`, but for the job's bookmark *at the time of this run* rather than its current one. Each entry has **`field_id`** (nullable), **`field_name`**, **`field_type`**, **`value`** (nullable), **`manual_override`** (boolean).
* **`webhook_deliveries`** (list) — what each [orchestration webhook](#orchestration-webhooks) did after this run, one entry per webhook and table, in the same shape as `list_webhook_deliveries`. Empty when there were none, or when you can see runs but not webhooks.
* **`meta.platform_url`** — link to the run in the platform; null if the run has no connection to link into.

</details>

<details>

<summary>list_sync_types</summary>

List the valid `sync_type` values a job can have, with their platform-facing description (e.g. `full` is labeled "Full Refresh") and a plain-language explanation of what each does. Not workspace-scoped — this is a small, fixed reference table.

**Example prompts**

* "What sync types are available for a job?"
* "What does the 'full' sync type mean?"

**Parameters**

* No parameters.

**Returns**

* **`result`** (list):
  * **`sync_type`** (string) — `full`, `incremental_append`, `incremental_overwrite`, or `incremental_versioned`; the same values `list_jobs` returns and `configure_jobs` takes.
  * **`description`** (string)
  * **`explanation`** (string)

</details>

<details>

<summary>list_sync_frequencies</summary>

List the valid `sync_frequency_id` values a job can have. Not workspace-scoped.

**Example prompts**

* "What sync frequency options can I choose for a job?"
* "Is there a manual, on-demand frequency option?"

**Parameters**

* No parameters.

**Returns**

* **`result`** (list):
  * **`sync_frequency_id`** (string)
  * **`description`** (string)
  * **`frequency_in_minutes`** (integer, nullable) — null for a manually-triggered, unscheduled frequency.

</details>

### Selecting jobs for trigger, cancel, enable, and disable

`trigger_run`, `cancel_run`, `enable_job`, and `disable_job` all choose the jobs they act on the same way. Pass `connection_id`, `job_ids` (from `list_jobs`), or both — at least one is required:

* **`connection_id` only** — acts on the connection's jobs (for `trigger_run`, every active top-level job; for the others, every job in the connection).
* **`job_ids` only** — acts on exactly those jobs, even if they belong to different connections (e.g. every job named "orders" across several connections).
* **Both** — acts on `job_ids`, but rejects the whole call if any of them don't belong to `connection_id`.

A job that can't be acted on is listed in `skipped` with a reason, and the rest of the call still goes through. The call only fails if nothing at all was acted on.

<details>

<summary>trigger_run</summary>

Trigger a manual run of a connection's jobs, or of specific jobs. See [Selecting jobs](#selecting-jobs-for-trigger-cancel-enable-and-disable) above for how `connection_id` and `job_ids` combine.

When only `connection_id` is given and the connection itself can't run (invalid credentials, or an execution window that blocks manual runs), the whole call fails. When `job_ids` are given, jobs whose connection has one of those problems are skipped instead. The call also fails if the workspace has no destination or the destination's credentials are invalid, or if the run would exceed the plan's monthly limit of manual runs.

**Example prompts**

* "Run all the jobs in the HubSpot connection now."
* "Re-run the 'orders' job with a full refresh."
* "Backfill the 'deals' job starting from January 1st."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, optional)
* **`job_ids`** (list of strings, optional)
* **`force_full_refresh`** (boolean, optional, default false) — re-extract the full history on incremental jobs instead of resuming from their cursor. Can't be combined with `raw_cursor`/`timestamp_cursor`.
* **`raw_cursor`** (string, optional) — override the job's incremental bookmark with this value for this run (custom backfill).
* **`timestamp_cursor`** (datetime, optional) — same as `raw_cursor`, for a timestamp bookmark. Pass only one of the two; both require a plan with custom backfill access.
* **`enable_delete`** (boolean, optional, default false) — also delete destination rows that no longer exist at the source during this run.

**Returns**

* **`triggered`** (list) — one entry per job that was queued: **`job_id`**, **`run_id`** (the new run, for `get_run` or `list_runs`), **`connection_id`**.
* **`skipped`** (list) — one entry per job that wasn't queued: **`job_id`**, **`reason`** — one of `not_found`, `inactive`, `webhook_triggered`, `invalid_connector_auth`, `window_blocked`, `already_running`, `source_removed` (its table/endpoint no longer exists at the source, so a run could only fail).

</details>

<details>

<summary>cancel_run</summary>

Cancel the currently queued, waiting, or running executions of a connection's jobs, or of specific jobs. See [Selecting jobs](#selecting-jobs-for-trigger-cancel-enable-and-disable) above for how `connection_id` and `job_ids` combine.

Canceling stops the job's running container (if any) and marks its execution as canceled. Data already written to the destination during the run is kept, and the job can be started again with `trigger_run`.

**Example prompts**

* "Cancel everything that's running on the Postgres connection."
* "Stop the 'orders' job's current run."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, optional)
* **`job_ids`** (list of strings, optional)

**Returns**

* **`canceled`** (list) — one entry per canceled job: **`job_id`**, **`run_id`** (the run that was canceled), **`connection_id`**.
* **`skipped`** (list) — one entry per job that wasn't canceled: **`job_id`**, **`reason`** — `not_found`, or `not_cancelable` if nothing was queued or running for it.

</details>

<details>

<summary>enable_job</summary>

Enable a connection's jobs, or specific jobs, so they run on their schedule again. See [Selecting jobs](#selecting-jobs-for-trigger-cancel-enable-and-disable) above for how `connection_id` and `job_ids` combine.

Enabling a job also enables every disabled job above it in the hierarchy (its parent, the parent's parent, and so on), the same as in the platform. Each job is checked against the workspace plan's limit on active jobs, counting those parent jobs too. Jobs are processed in the order given, so if the limit is reached partway through, the earlier jobs are enabled and the rest are skipped.

Enabling also checks the job's rules again: a CDC job can't be enabled while its connection's CDC is turned off, and a job whose table/endpoint no longer exists at the source isn't enabled, since its runs could only fail.

**Example prompts**

* "Turn the 'orders' and 'customers' jobs back on."
* "Re-enable every job in the Salesforce connection."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, optional)
* **`job_ids`** (list of strings, optional)

**Returns**

* **`enabled`** (list) — one entry per enabled job: **`job_id`**, **`connection_id`**, **`cascaded_job_ids`** (list of strings — the parent jobs that were enabled along with it).
* **`skipped`** (list) — one entry per job that wasn't enabled: **`job_id`**, **`reason`**, and **`detail`** (nullable, how to fix it, where there is a fix). Reasons:
  * `not_found`
  * `already_enabled`
  * `enabled_via_cascade` — enabled as a parent of an earlier job in the same call, so it ends up enabled too.
  * `jobs_limit_exceeded`
  * `cdc_unavailable` — a CDC job on a connection whose CDC was turned off.
  * `source_removed` — its table/endpoint no longer exists at the source.

</details>

<details>

<summary>disable_job</summary>

Disable a connection's jobs, or specific jobs, so they stop running on their schedule. See [Selecting jobs](#selecting-jobs-for-trigger-cancel-enable-and-disable) above for how `connection_id` and `job_ids` combine.

Disabling a job also disables every job below it in the hierarchy, and cancels their queued or waiting next runs. A run that's already executing is left alone — use `cancel_run` to stop it.

**Example prompts**

* "Pause every job in the HubSpot connection."
* "Disable the 'events' job, it's not needed anymore."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, optional)
* **`job_ids`** (list of strings, optional)

**Returns**

* **`disabled`** (list) — one entry per disabled job: **`job_id`**, **`connection_id`**, **`cascaded_job_ids`** (list of strings — the child jobs that were disabled along with it).
* **`skipped`** (list) — one entry per job that wasn't disabled: **`job_id`**, **`reason`** — one of `not_found`, `already_disabled`, `disabled_via_cascade` (already disabled as a child of another job in the same call).

</details>

## Schema Discovery

<details>

<summary>list_endpoints</summary>

List the endpoints (resources/tables, e.g. "deals", "contacts") an API connector exposes.

**Example prompts**

* "What endpoints does our HubSpot connector expose?"
* "Does the 'deals' endpoint have any child endpoints?"

**Parameters**

* **`workspace_id`** (string, required)
* **`source_connector_id`** (string, required)
* **`endpoint_id`** (string, optional) — filter to one endpoint, e.g. to look up an `api_endpoint_id` from `list_jobs`.
* **`parent_endpoint_id`** (string, optional) — filters to the direct children of one endpoint, for walking the hierarchy top-down; omit to see all of them flattened.
* **`name`** (string, optional) — substring search (e.g. `name='deal'`).
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per endpoint:
  * **`endpoint_id`** (string)
  * **`connector_id`** (string)
  * **`name`** (string)
  * **`parent_endpoint_id`** (string, nullable)
  * **`description`** (string, nullable)
  * **`has_children_endpoints`** (boolean) — whether drilling into it with `parent_endpoint_id` is worth it.
  * **`has_custom_fields`** (boolean) — whether the endpoint also picks up a connection's own custom fields (see `list_custom_fields`).
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

<details>

<summary>list_endpoint_fields</summary>

List the base fields (the connector type's own schema) of one endpoint, or of every endpoint in an API connection. A connection's own custom fields are listed by `list_custom_fields`.

**Example prompts**

* "What fields are on the 'deals' endpoint?"
* "Which fields in this HubSpot connection are dates?"

**Parameters**

* **`workspace_id`** (string, required)
* **`endpoint_id`** (string, optional) — one endpoint, from `list_endpoints`.
* **`connection_id`** (string, optional) — every endpoint of this API connection, when passed without `endpoint_id`. At least one of the two is required.
* **`name`** (string, optional) — substring search (e.g. `name='owner'`).
* **`field_type`** (string, optional) — case-insensitive substring match on the type the connector reports (e.g. `field_type='date'` matches `date` and `datetime`).
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per field:
  * **`field_id`** (string)
  * **`connector_id`** (string)
  * **`endpoint_id`** (string)
  * **`endpoint_name`** (string)
  * **`name`** (string)
  * **`field_type`** (string, nullable) — whatever the connector itself reports; free text, not a fixed set of values.
  * **`is_nullable`** (boolean)
  * **`example`** (string, nullable)
  * **`description`** (string, nullable)
  * **`is_primary_key`** (boolean)
  * **`is_cursor_available`** (boolean)
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

<details>

<summary>list_custom_fields</summary>

List an API connection's own custom fields (e.g. a CRM's custom properties) — fields beyond its connector type's base schema, which `list_endpoint_fields` lists.

**Example prompts**

* "What custom properties does our HubSpot account have?"
* "Which custom fields does the 'deals' endpoint pick up?"

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, required)
* **`endpoint_id`** (string, optional) — narrow to the custom fields that endpoint picks up (none unless `list_endpoints` says `has_custom_fields` is true). Without it, every custom field of the connection is listed.
* **`name`** (string, optional) — substring search.
* **`field_type`** (string, optional) — case-insensitive substring match on the type the source reports.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per custom field:
  * **`field_id`** (string)
  * **`connection_id`** (string)
  * **`field_key`** (string)
  * **`name`** (string)
  * **`field_type`** (string, nullable)
  * **`is_nullable`** (boolean)
  * **`example`** (string, nullable)
  * **`description`** (string, nullable)
  * **`endpoint`** (string) — the endpoint text the field was recorded against.
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

<details>

<summary>list_table_schemas</summary>

Summarize one database connection's tables by schema — a cheap first look at a large database, e.g. to ask which schemas to sync before passing them to `configure_jobs`.

**Example prompts**

* "Which schemas does our Postgres database have, and how big are they?"

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, required)

**Returns**

* **`results`** (list) — one entry per schema:
  * **`schema_name`** (string, nullable) — null for tables outside any schema.
  * **`table_count`** (integer)

</details>

<details>

<summary>list_tables</summary>

List the tables (e.g. "public.orders") one database connection exposes. Only tables whose columns have already been introspected are returned.

**Example prompts**

* "What tables are available in our Postgres connection?"
* "Show me the tables in the 'public' schema."

**Parameters**

* **`workspace_id`** (string, required)
* **`connection_id`** (string, required)
* **`table_id`** (string, optional) — filter to one table, e.g. to look up a `company_table_id` from `list_jobs`.
* **`schema_name`** (string, optional) — filters to one schema (e.g. `'public'`); omit to see tables across all schemas.
* **`name`** (string, optional) — substring search on the table name (e.g. `name='order'`).
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per table:
  * **`table_id`** (string)
  * **`connection_id`** (string)
  * **`schema_name`** (string, nullable)
  * **`name`** (string)
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

<details>

<summary>list_table_columns</summary>

List the columns of one table, or of every table in a database connection (e.g. to find all its date columns at once). Each column names its table.

**Example prompts**

* "What columns does the 'orders' table have?"
* "Which columns in our database could track when a row last changed?"

**Parameters**

* **`workspace_id`** (string, required)
* **`table_id`** (string, optional) — one table, from `list_tables`.
* **`connection_id`** (string, optional) — every table of this connection, when passed without `table_id`. At least one of the two is required.
* **`name`** (string, optional) — substring search (e.g. `name='email'`).
* **`field_type`** (string, optional) — case-insensitive substring match on the column's type as the database reports it (e.g. `field_type='date'` matches `DATE` and `DATETIME`; `'time'` matches `TIMESTAMP` too).
* **`cursor_candidates_only`** (boolean, optional, default false) — keep only the columns usable to track changes by (the ones `configure_jobs`' `cursor_column` accepts), e.g. to pick one for tables in `needs_decision`.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per column:
  * **`column_id`** (string)
  * **`connection_id`** (string)
  * **`table_id`** (string)
  * **`schema_name`** (string, nullable)
  * **`table_name`** (string)
  * **`name`** (string)
  * **`field_type`** (string)
  * **`is_nullable`** (boolean)
  * **`example`** (string, nullable)
  * **`description`** (string, nullable)
  * **`comments`** (string, nullable)
  * **`is_primary_key`** (boolean)
  * **`is_cursor_available`** (boolean)
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

## Metrics

<details>

<summary>get_workspace_metrics</summary>

Aggregate a workspace's metrics over time, broken down by time and by entity (workspace-wide, per connection, or per job).

**Example prompts**

* "How many rows have we processed this month?"
* "Which connections had the most errors last week?"
* "What does our sync activity look like by hour of day?"

**Parameters**

* **`workspace_id`** (string, required)
* **`metric`** (string, required) — what's counted: `rows` (rows processed), `retries` (source API retries), `runs` (runs no longer queued/waiting/executing), `successes` (finished runs), `errors` (failed runs).
* **`time_level`** (string, required) — `hourly`, `daily`, `monthly` (a timeline); or `weekday`/`hour_of_day` (fold the whole range into 7/24 fixed buckets to show a recurring pattern). Buckets use the user's own timezone, not UTC.
* **`entity_level`** (string, required) — `workspace` (one total per bucket), `connection`, or `job` (broken out per connection/job).
* **`entity_id`** (string, optional) — scope to just one connection/job (not valid with `entity_level='workspace'`).
* **`start_date`** (string, optional, ISO 8601) — clamped to the workspace's plan-based retention window if it asks further back than the plan allows.
* **`end_date`** (string, optional, ISO 8601, exclusive) — the range defaults to, and is capped at, a window sized by `time_level`/`entity_level` (tighter for finer buckets and for jobs), even for `weekday`/`hour_of_day`.
* **`entity_limit`** (integer, optional, default 10, max 50) — without `entity_id`, how many entities to return, ranked by their total over the range.
* **`entity_offset`** (integer, optional) — pages further down the same ranking.
* **`rank_order`** (string, optional, default `top`) — `top` for the busiest entities, `bottom` for the least active ones (e.g. finding idle jobs).

**Returns**

* **`results`** (list) — one entry per time bucket (and per entity, if broken out): **`time_bucket`** (string), **`value`** (integer), **`entity_id`** (string, nullable).
* **`entities`** (map) — keyed by `entity_id`, each with **`connection_name`** (nullable), **`job_name`** (nullable), **`connection_id`** (nullable, a job's parent connection), **`rank`** (integer, nullable, its 1-indexed position in the full ranking), **`total`** (integer, nullable, its summed value over the whole range), **`deleted`** (boolean, nullable, true if the connection/job has since been deleted — its history still counts, but it's gone from `list_connections` and the platform).
* **`paywalled_since`** (string, nullable) — set if `start_date` was clamped by the plan's retention window.
* **`meta.start_date`** / **`meta.end_date`** / **`meta.entity_limit`** / **`meta.entity_offset`** / **`meta.timezone`** / **`meta.results_count`** — the values actually applied, not just an echo of your input.

</details>

## Orchestration Webhooks

An orchestration webhook is an HTTP request Erathos sends after a job's run finishes, to start the next step in another tool, such as an Airflow DAG or a dbt Cloud job. It's sent once per table the run synced, and only when all of its rules pass. A webhook covers the jobs it's linked to, and every job of the connections it's linked to, including jobs added to them later. See [Trigger jobs outside Erathos](/api/introduction/trigger-jobs-outside-erathos.md) for how webhooks work.

These tools need your workspace's plan to include orchestration. A few safeguards apply when the agent writes webhooks:

* **Credentials are never typed into the conversation.** A webhook references them as `${{secrets.NAME}}`, and the agent refuses a header or URL with a literal credential in it. Secrets are created through the [developer API](/api/concepts/variables-and-secrets.md) for now, so their values never pass through the agent.
* **The agent confirms the destination with you** before creating a webhook or changing where it's sent, since the request carries details of your runs.
* **Webhooks only go to public addresses.** A webhook pointing to a private, loopback or link-local address is refused when it's sent.

<details>

<summary>list_orchestration_webhooks</summary>

List a workspace's orchestration webhooks, with each one's last delivery.

**Example prompts**

* "Which webhooks fire after my Postgres connection's jobs?"
* "Are any of my webhooks failing?"

**Parameters**

* **`workspace_id`** (string, required)
* **`job_id`** (string, optional) — webhooks that fire for this job, linked to it or to its connection.
* **`connection_id`** (string, optional) — webhooks linked to this connection or to any of its jobs.
* **`is_active`** (boolean, optional)
* **`webhook_id`** (string, optional) — filter to one webhook.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per webhook:
  * **`webhook_id`** (string)
  * **`description`** (string)
  * **`is_active`** (boolean)
  * **`method`** (string)
  * **`target_host`** (string, nullable) — the URL's host as written, which may be a `${{variables.NAME}}` reference.
  * **`job_count`** (integer) / **`connection_count`** (integer) — what it's linked to.
  * **`rule_count`** (integer)
  * **`created_at`** / **`updated_at`** (datetime)
  * **`last_delivery_status`** (string, nullable) / **`last_delivery_at`** (datetime, nullable) — its most recent delivery within your plan's history window.
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

<details>

<summary>get_orchestration_webhook</summary>

Show one webhook in full: its request, rules, and the jobs and connections it fires for.

**Example prompts**

* "Show me the full configuration of the Airflow webhook."
* "Which rules decide when this webhook is sent?"

**Parameters**

* **`workspace_id`** (string, required)
* **`webhook_id`** (string, required)

**Returns**

* **`webhook_id`**, **`description`**, **`is_active`**, **`method`** — as in `list_orchestration_webhooks`.
* **`url`** (string), **`header`** (object, nullable), **`body`** (object, nullable) — may contain `${{variables.NAME}}`, `${{secrets.NAME}}` and `${{erathos.name}}` references. A credential written literally instead of as a secret reference is replaced by `[redacted]`.
* **`redacted_fields`** (list) — where something was redacted (e.g. `header.Authorization`). The agent can suggest moving it into a secret.
* **`rules`** (list) — each with **`variable_name`**, **`operation`** and **`value`** (a string, or a list for `IN`/`NOT_IN`). Every rule must pass for the webhook to be sent.
* **`job_ids`** (list) / **`connection_ids`** (list) — what it fires for.
* **`created_at`** / **`updated_at`** (datetime)

</details>

<details>

<summary>configure_orchestration_webhook</summary>

Create a webhook, or change one. Without `webhook_id` it creates one; with it, only the fields passed change. To stop a webhook firing, disable it with `is_active: false`, which can be undone, rather than deleting it.

**Example prompts**

* "After my orders table syncs successfully, trigger the `daily_orders` DAG in Airflow."
* "Make that webhook fire for every job of the Postgres connection, including new tables."
* "Only send it when rows were loaded."
* "Pause the dbt webhook."

**Parameters**

* **`workspace_id`** (string, required)
* **`webhook_id`** (string, optional) — the webhook to change; omit to create one.
* **`description`** (string, optional) — required when creating.
* **`url`** (string, optional) — required when creating. `http://` or `https://`, and may contain references anywhere, the host included.
* **`method`** (string, optional) — `POST`, `PUT`, `PATCH`, `GET` or `DELETE`. Required when creating.
* **`header`** (object, optional) — e.g. `{"Authorization": "Bearer ${{secrets.AIRFLOW_TOKEN}}"}`. Pass `{}` to clear it.
* **`body`** (object, optional) — pass `{}` to clear it.
* **`rules`** (list, optional) — each `{variable_name, operation, value}`, using the operations and value formats `list_run_metadata_variables` gives. Replaces all current rules when passed; `[]` removes them, so the webhook fires after every run.
* **`job_ids`** (list, optional) / **`connection_ids`** (list, optional) — what it fires for. A webhook needs at least one job or connection. Each replaces the current list when passed.
* **`is_active`** (boolean, optional)

The agent can't save a webhook that has a literal credential, references a variable, secret or metadata field that doesn't exist, has a reference missing its `$`, or points to an obviously non-public address. It explains what to fix instead.

**Returns**

* **`created`** (boolean) — whether a new webhook was created.
* **`target_host`** (string, nullable) — where it sends, as written.
* **`webhook`** — the saved webhook, in the same shape as `get_orchestration_webhook`.

</details>

<details>

<summary>delete_orchestration_webhook</summary>

Delete a webhook for good. Your AI client should ask you to confirm before it runs; to only stop a webhook firing, disable it with `configure_orchestration_webhook` instead.

**Example prompts**

* "Delete the old test webhook."

**Parameters**

* **`workspace_id`** (string, required)
* **`webhook_id`** (string, required)

**Returns**

* **`webhook_id`** (string)
* **`description`** (string)
* **`deleted`** (boolean)

</details>

<details>

<summary>list_webhook_deliveries</summary>

List what happened to webhooks after each run, newest first: one entry per webhook, run and table. It answers "why didn't my webhook fire?" and "is it failing?".

**Example prompts**

* "Why didn't my Airflow DAG start after last night's sync?"
* "Has the dbt webhook failed this week?"
* "Show me the webhook deliveries for this run."

**Parameters**

* **`workspace_id`** (string, required)
* **`webhook_id`** (string, optional)
* **`job_id`** (string, optional)
* **`connection_id`** (string, optional) — deliveries for any job of this connection.
* **`run_id`** (string, optional) — the same `run_id` the run tools take.
* **`status`** (string, optional) — `SKIPPED`, `SUCCEEDED`, `FAILED` or `BLOCKED`.
* **`target_host`** (string, optional) — the resolved host, e.g. `airflow.acme.com`.
* **`delivered_after`** / **`delivered_before`** (datetime, optional) — clamped to your plan's history window, like runs.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — one entry per delivery:
  * **`delivery_id`**, **`webhook_id`**, **`webhook_description`**
  * **`status`** (string) — `SKIPPED` (its rules didn't pass), `SUCCEEDED` (sent, 2xx response), `FAILED` (non-2xx response, timeout or network error), or `BLOCKED` (refused because the host isn't a public address).
  * **`failed_rules`** (list, nullable) — for `SKIPPED`: each rule that didn't pass, with **`variable_name`**, **`operation`**, **`expected`** and the run's **`actual`** value.
  * **`run_id`**, **`job_id`**, **`table`** (string)
  * **`delivered_at`** (datetime)
  * **`target_host_template`** (string, nullable) — the host as written; **`target_host`** (string, nullable) — the resolved host, null when it came from a secret; **`target_host_ip`** (string, nullable)
  * **`http_status`** (integer, nullable), **`error`** (string, nullable), **`duration_ms`** (number, nullable)
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.
* **`meta.paywalled_since`** (datetime, nullable) — set when your plan's history window hid older deliveries.

</details>

<details>

<summary>list_run_metadata_variables</summary>

List the run metadata a webhook can use: each value's `${{erathos.name}}` placeholder for the URL, header or body, and, for those rules can use, the operations and value format a rule takes. Not workspace-scoped.

**Example prompts**

* "What run details can I send in a webhook?"
* "How do I write a rule that only fires on weekdays?"

**Parameters**

* **`usable_in_rules`** (boolean, optional) — only metadata rules can, or can't, use.

**Returns**

* **`results`** (list):
  * **`name`** (string) and **`placeholder`** (string), e.g. `${{erathos.rows}}`
  * **`description`** (string)
  * **`data_type`** (string)
  * **`usable_in_rules`** (boolean)
  * **`rule_variable_name`** (string, nullable) — the rule's `variable_name`, e.g. `ROWS`.
  * **`rule_operations`** (list) — the operations a rule on it accepts.
  * **`rule_value_format`** (string, nullable) — how to write the rule's value.

</details>

<details>

<summary>list_workspace_variables</summary>

List a workspace's variables: named values webhooks reference as `${{variables.NAME}}`, such as a tool's base URL. Values are shown.

**Example prompts**

* "Which variables are set up in this workspace?"

**Parameters**

* **`workspace_id`** (string, required)
* **`name`** (string, optional) — a substring search on the name.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — **`variable_id`**, **`name`**, **`value`**, **`placeholder`** (e.g. `${{variables.AIRFLOW_HOST}}`), **`created_at`**, **`updated_at`**.
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

<details>

<summary>list_workspace_secrets</summary>

List a workspace's secrets by name: credentials webhooks reference as `${{secrets.NAME}}`. Values are never shown, here or anywhere else.

**Example prompts**

* "Is there already a secret for the Airflow token?"

**Parameters**

* **`workspace_id`** (string, required)
* **`name`** (string, optional) — a substring search on the name.
* **`limit`** (integer, optional)
* **`offset`** (integer, optional)

**Returns**

* **`results`** (list) — **`secret_id`**, **`name`**, **`placeholder`** (e.g. `${{secrets.AIRFLOW_TOKEN}}`), **`created_at`**, **`updated_at`** (nullable for older secrets).
* **`meta.limit`** / **`meta.offset`** / **`meta.has_more`** — pagination.

</details>

## Support

<details>

<summary>contact_support</summary>

Open a ticket with the Erathos support team on your behalf, for a problem the agent couldn't solve with the other tools. It's the same kind of ticket the chat in the platform opens, so you follow it under **Recent tickets** in the platform's chat, and replies reach you there and by email. They can't be read back through the agent.

The agent calls this only when you ask for human help, or agree to it after it couldn't fix the problem. It should show you the title and message before sending. Erathos adds the details of the run, job and connection involved (status, errors and a link to the platform) itself, so you don't need to copy them in. Values that look like credentials are masked before sending, but don't put passwords or keys in the message.

You can open at most one ticket per run and 5 tickets per day.

**Example prompts**

* "This run keeps failing and I don't know why. Open a ticket with Erathos support."
* "The orders table is missing rows even though the job succeeds. Ask support to look into it."
* "Request a Pipedrive source connector from Erathos."

**Parameters**

* **`workspace_id`** (string, required)
* **`title`** (string, required) — a short summary of the problem, used as the ticket's title (up to 200 characters).
* **`message`** (string, required) — the problem in your words, plus what the agent found (up to 5,000 characters).
* **`category`** (string, optional, default `other`) — picks the ticket type:
  * `run_failure` — a failed run. Requires `run_id`.
  * `data_inconsistency` — runs succeed but rows are missing, extra or wrong. Requires `job_id`, `run_id` or `connection_id`.
  * `connector_request` — a connector Erathos doesn't have yet. Requires `connector_request`.
  * `feature_request` — an idea for a new feature.
  * `connection_setup`, `billing` or `other` — anything else.
* **`run_id`** (string, optional) — the run the ticket is about, from `list_runs` or `get_run`.
* **`job_id`** (string, optional) — the job the ticket is about, from `list_jobs`.
* **`connection_id`** (string, optional) — the connection the ticket is about, from `list_connections`. When more than one of `run_id`, `job_id` and `connection_id` is given, they must belong to the same connection.
* **`steps_already_tried`** (string, optional) — what was already done, e.g. re-ran the job or refreshed the credentials.
* **`connector_request`** (object, optional) — only for `connector_request`:
  * **`connector_name`** (string, required) — the tool or database you want to connect.
  * **`kind`** (string, required) — `api` or `database`.
  * **`role`** (string, required) — `source` (extract data from it) or `destination` (load data into it).
  * **`documentation_url`** (string, optional) — the API's documentation.
  * **`endpoints`** (string, optional) — the endpoints you need data from.

**Returns**

* **`ticket_id`** (string) — the ticket number, as shown in the platform's chat.
* **`ticket_type`** (string) — e.g. `Failed job`, `Data inconsistencies`, `Connector request`, `Feature request` or `General ticket`.
* **`status`** (string) — the ticket's status as you see it, e.g. `Submitted`.

</details>
