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

# GraphQL API

Honeydew provides a GraphQL API, that allows to directly call the Honeydew service.

<Note>
  This guide covers a direct connection to the Honeydew API using GraphQL.

  It is also possible to use Honeydew APIs with a
  [Snowflake Connection](https://docs.snowflake.com/en/developer-guide/python-connector/python-connector),
  by calling the [Snowflake Native Application](/docs/integration/snowflake-native-app) API.
</Note>

<Warning>
  Public GraphQL API is not enabled by default. To enable for your organization, please contact [support@honeydew.ai](mailto:support@honeydew.ai).
</Warning>

## Use Cases

### Embedded Analytics

The GraphQL API can be used to build embedded analytics applications that leverage the semantic layer.
This allows you to build custom applications that use the semantic layer to generate queries and retrieve data.

### Building custom AI analyst applications

The GraphQL API can be used to build custom AI analyst applications that leverage the semantic layer
to answer user questions in natural language.

### Metadata Sharing

The GraphQL API can be used to share metadata with other applications or services.
For example, you can use the GraphQL API to share metadata with a data catalog or a data governance tool,
or to import metadata from another system into Honeydew.

### BI Integration

The GraphQL API can be used to publish metadata to BI tools, such as Tableau, Power BI, Thoughtspot, and more.

### Development and Testing

The GraphQL API can be used to develop and test semantic layer definitions.
Developers can use their favorite development tools to edit the semantic layer definitions, push them directly to git,
and use the GraphQL APIs to validate the definitions.
These validations can also be integrated into CI/CD pipelines to ensure that the semantic layer definitions
are valid before deploying them to production.

## Usage

### Security

You can use the GraphQL API with an [API Key and Secret](/docs/initial-setup#api-keys).
You cannot use the GraphQL API with a Honeydew username and password.

<Note>
  Different API queries and mutations require different permissions. Therefore, in order to follow
  the principal of least privileges as a best practice, you may need to create different API keys for different use cases.

  Each query or mutation below specifies the required permissions.
</Note>

<Tip>
  It is recommended to restrict API access to specific IP addresses or ranges,
  by using the [IP Access Control](/docs/access-control/ip-access-control) feature.
  To set up IP Access Control, please contact [support@honeydew.ai](mailto:support@honeydew.ai).
</Tip>

### API Endpoint

The default API endpoint is `https://api.honeydew.cloud/api/public/v1/graphql`.
If your organization uses a custom hostname for the API connection,
you can locate it in the Honeydew UI, under the **API** section in **Settings**.

### Headers

The following custom Honeydew headers can be used in the API requests:

* `X-Honeydew-Client`: A string that identifies the client making the request.
  Use it to identify the client in Honeydew logs and query history, for audit, debugging and support purposes.
* `X-Honeydew-Workspace`: The name of the workspace to use for the request.
  This is required for most queries and mutations that operate on a specific workspace.
* `X-Honeydew-Branch`: The name of the branch to use for the request.
  This is required for most queries and mutations that operate on a specific branch.

### Rate Limiting

The Public API implements rate limiting to ensure fair usage and system stability. Rate limits are applied per user (or per API key) and are enforced according to industry standards.

**Default Rate Limit**: 60 calls per user/key per minute

**Response Headers**: The API provides rate limit information through standard HTTP response headers:

* `RateLimit-Limit`: The maximum number of requests allowed per time window
* `RateLimit-Remaining`: The number of requests remaining in the current time window
* `RateLimit-Reset`: The timestamp when the rate limit will reset (in Unix epoch seconds)

<Tip>
  Monitor these headers in your API responses to implement proper rate limiting in your applications and avoid hitting rate limits unexpectedly.
</Tip>

<Warning>
  When you exceed the rate limit, the API will return a `429 Too Many Requests` HTTP status code. Implement exponential backoff and retry logic in your applications to handle rate limiting gracefully.
</Warning>

<Note>
  If you need to increase your rate limits beyond the default, please contact [support@honeydew.ai](mailto:support@honeydew.ai) to discuss your requirements.
</Note>

### API integration example

Below we have provided API integration examples for Python, JavaScript and cURL.
The API can be used with any programming language that supports HTTP requests.

<Tip>
  To learn more about GraphQL, you can refer to the [GraphQL documentation](https://graphql.org/learn/)
</Tip>

<Tabs>
  <Tab title="Python">
    <Note>
      This example uses the `requests` library to make HTTP requests to the Honeydew API.
      To install it, run:

      ```bash theme={null}
      pip install requests
      ```
    </Note>

    ```python theme={null}
    import requests
    from requests.auth import HTTPBasicAuth

    # Replace with your Honeydew API Key and Secret
    API_KEY = "your_api_key_here"
    API_SECRET = "your_api_secret_here"


    # Honeydew API endpoint
    # If your organization uses a custom hostname, use it here instead of the default
    HONEYDEW_API_HOSTNAME = "api.honeydew.cloud"
    API_URI = f"https://{HONEYDEW_API_HOSTNAME}/api/public/v1/graphql"


    # Example of a GraphQL query to get workspaces
    # Since the graphql call is at a global level, we don't need to specify workspace or branch here

    WORKSPACES_QUERY = """
        query {
            workspaces {
                name
            }
        }
    """

    response = requests.post(
        API_URI,
        json={"query": WORKSPACES_QUERY},
        auth=HTTPBasicAuth(API_KEY, API_SECRET),
        timeout=30,
        headers={
            "Content-Type": "application/json",
            "X-Honeydew-Client": "python-script",
        },
    )
    response.raise_for_status()  # Raise an error for bad responses

    print("Response from Honeydew API:", response.json())


    # Example of a GraphQL query to get entities of a specific workspace

    WORKSPACE_NAME = "tpch"
    BRANCH_NAME = "prod"

    ENTITIES_QUERY = """
        query {
            entities {
                name
            }
        }
    """

    response = requests.post(
        API_URI,
        json={"query": ENTITIES_QUERY},
        auth=HTTPBasicAuth(API_KEY, API_SECRET),
        timeout=30,
        headers={
            "Content-Type": "application/json",
            "X-Honeydew-Client": "python-script",
            "X-Honeydew-Workspace": WORKSPACE_NAME,
            "X-Honeydew-Branch": BRANCH_NAME,
        },
    )
    response.raise_for_status()  # Raise an error for bad responses

    print("Response from Honeydew API:", response.json())


    # Example of a GraphQL query to get a specific entity with variables
    ENTITY_QUERY = """
        query getEntity($name: String!) {
            entity(name: $name) {
                name
                keys
            }
        }
    """

    response = requests.post(
        API_URI,
        json={
            "query": ENTITY_QUERY,
            "variables": {"name": "customers"},
        },
        auth=HTTPBasicAuth(API_KEY, API_SECRET),
        timeout=30,
        headers={
            "Content-Type": "application/json",
            "X-Honeydew-Client": "python-script",
            "X-Honeydew-Workspace": WORKSPACE_NAME,
            "X-Honeydew-Branch": BRANCH_NAME,
        },
    )
    response.raise_for_status()  # Raise an error for bad responses

    print("Response from Honeydew API:", response.json())

    # Example of a GraphQL mutation to reload workspace
    RELOAD_ALL_WORKSPACES = """
        mutation ReloadAllWorkspaces {
            reset_all_workspaces
        }
    """

    response = requests.post(
        API_URI,
        json={
            "query": RELOAD_ALL_WORKSPACES,
        },
        auth=HTTPBasicAuth(API_KEY, API_SECRET),
        timeout=30,
        headers={
            "Content-Type": "application/json",
        },
    )
    response.raise_for_status()  # Raise an error for bad responses

    print("Response from Honeydew API:", response.json())
    ```

    The above will produce the following kind of output:

    ```
    Response from Honeydew API: {'data': {'workspaces': [{'name': 'tasty_bytes'}, {'name': 'tpch'}]}}
    Response from Honeydew API: {'data': {'entities': [{'name': 'customers'}, {'name': 'order_lines'}, {'name': 'orders'}, {'name': 'parts'}, {'name': 'sessions'}]}}
    Response from Honeydew API: {'data': {'entity': {'keys': ['custkey'], 'name': 'customers'}}}
    Response from Honeydew API: {'data': {'reset_all_workspaces': None}}
    ```

    <Note>
      The above examples are basic and do not include error handling.
      In production code, you should handle errors and exceptions appropriately.
      We recommended using a graphql client library for more complex queries and mutations.
    </Note>
  </Tab>

  <Tab title="JavaScript">
    <Note>
      This example uses the `axios` package to make HTTP requests to the Honeydew API.
      To install it, run:

      ```bash theme={null}
      npm install axios
      ```
    </Note>

    ```JavaScript theme={null}
    const axios = require('axios');

    // Replace with your Honeydew API Key and Secret
    const API_KEY = "your_api_key_here";
    const API_SECRET = "your_api_secret_here";

    // Honeydew API endpoint
    // If your organization uses a custom hostname, use it here instead of the default
    const HONEYDEW_API_HOSTNAME = "api.honeydew.cloud";
    const API_URI = `https://${HONEYDEW_API_HOSTNAME}/api/public/v1/graphql`;

    // Example of a GraphQL query to get workspaces
    // Since the graphql call is at a global level, we don't need to specify workspace or branch here
    const WORKSPACES_QUERY = `
        query {
            workspaces {
                name
            }
        }
    `;

    axios
      .post(
        API_URI,
        { query: WORKSPACES_QUERY },
        {
          auth: {
            username: API_KEY,
            password: API_SECRET,
          },
          timeout: 30000,
          headers: {
            "Content-Type": "application/json",
            "X-Honeydew-Client": "javascript-app",
          },
        }
      )
      .then((response) => {
        console.log(
          "Response from Honeydew API: " + JSON.stringify(response.data, null, 2)
        );
      });

    // Example of a GraphQL query to get entities of a specific workspace
    const WORKSPACE_NAME = "tpch";
    const BRANCH_NAME = "prod";

    const ENTITIES_QUERY = `
        query {
            entities {
                name
            }
        }
    `;

    axios
      .post(
        API_URI,
        { query: ENTITIES_QUERY },
        {
          auth: {
            username: API_KEY,
            password: API_SECRET,
          },
          timeout: 30000,
          headers: {
            "Content-Type": "application/json",
            "X-Honeydew-Client": "javascript-app",
            "X-Honeydew-Workspace": WORKSPACE_NAME,
            "X-Honeydew-Branch": BRANCH_NAME,
          },
        }
      )
      .then((response) => {
        console.log(
          "Response from Honeydew API: " + JSON.stringify(response.data, null, 2)
        );
      });

    // Example of a GraphQL query to get a specific entity with variables
    const ENTITY_QUERY = `
        query getEntity($name: String!) {
            entity(name: $name) {
                name
                keys
            }
        }
    `;

    axios
      .post(
        API_URI,
        {
          query: ENTITY_QUERY,
          variables: { name: "customers" },
        },
        {
          auth: {
            username: API_KEY,
            password: API_SECRET,
          },
          timeout: 30000,
          headers: {
            "Content-Type": "application/json",
            "X-Honeydew-Client": "javascript-app",
            "X-Honeydew-Workspace": WORKSPACE_NAME,
            "X-Honeydew-Branch": BRANCH_NAME,
          },
        }
      )
      .then((response) => {
        console.log(
          "Response from Honeydew API: " + JSON.stringify(response.data, null, 2)
        );
      });

    // Example of a GraphQL mutation to reload workspace
    const RELOAD_ALL_WORKSPACES = `
        mutation ReloadAllWorkspaces {
            reset_all_workspaces
        }
    `;

    axios
      .post(
        API_URI,
        { query: RELOAD_ALL_WORKSPACES },
        {
          auth: {
            username: API_KEY,
            password: API_SECRET,
          },
          timeout: 30000,
          headers: {
            "Content-Type": "application/json",
            "X-Honeydew-Client": "javascript-app",
          },
        }
      )
      .then((response) => {
        console.log(
          "Response from Honeydew API: " + JSON.stringify(response.data, null, 2)
        );
      });
    ```

    The above will produce the following kind of output:

    ```
    Response from Honeydew API: {
      "data": {
        "workspaces": [
          {
            "name": "tasty_bytes"
          },
          {
            "name": "tpch"
          }
        ]
      }
    }
    Response from Honeydew API: {
      "data": {
        "entities": [
          {
            "name": "customers"
          },
          {
            "name": "order_lines"
          },
          {
            "name": "orders"
          },
          {
            "name": "parts"
          },
          {
            "name": "sessions"
          }
        ]
      }
    }
    Response from Honeydew API: {
      "data": {
        "entity": {
          "keys": [
            "custkey"
          ],
          "name": "customers"
        }
      }
    }
    Response from Honeydew API: {
      "data": {
        "reset_all_workspaces": null
      }
    }
    ```

    <Note>
      The above examples are basic and do not include error handling.
      In production code, you should handle errors and exceptions appropriately.
      We recommended using a graphql client library for more complex queries and mutations.
    </Note>
  </Tab>

  <Tab title="cURL">
    <Note>
      This example uses `curl` to make HTTP requests to the Honeydew API.
      `curl` is typically pre-installed on most systems.
    </Note>

    ```bash theme={null}
    # Replace with your Honeydew API Key and Secret
    export API_KEY="your_api_key_here"
    export API_SECRET="your_api_secret_here"

    # Honeydew API endpoint
    # If your organization uses a custom hostname, use it here instead of the default
    export HONEYDEW_API_HOSTNAME="api.honeydew.cloud"
    export API_URI="https://${HONEYDEW_API_HOSTNAME}/api/public/v1/graphql"

    echo "=== Example 1: Querying workspaces ==="
    # Example of a GraphQL query to get workspaces
    # Since the graphql call is at a global level, we don't need to specify workspace or branch here
    export WORKSPACES_QUERY='{
      "query": "query { workspaces { name } }"
    }'

    curl -X POST "${API_URI}" \
      -H "Content-Type: application/json" \
      -H "X-Honeydew-Client: curl-script" \
      -u "${API_KEY}:${API_SECRET}" \
      -d "${WORKSPACES_QUERY}" \
      --max-time 30


    echo -e "\n\n=== Example 2: Querying entities of a specific workspace ==="
    # Example of a GraphQL query to get entities of a specific workspace
    export WORKSPACE_NAME="tpch"
    export BRANCH_NAME="prod"
    export ENTITIES_QUERY='{
      "query": "query { entities { name } }"
    }'

    curl -X POST "${API_URI}" \
      -H "Content-Type: application/json" \
      -H "X-Honeydew-Client: curl-script" \
      -H "X-Honeydew-Workspace: ${WORKSPACE_NAME}" \
      -H "X-Honeydew-Branch: ${BRANCH_NAME}" \
      -u "${API_KEY}:${API_SECRET}" \
      -d "${ENTITIES_QUERY}" \
      --max-time 30


    echo -e "\n\n=== Example 3: Querying a specific entity with variables ==="
    # Example of a GraphQL query to get a specific entity with variables
    export ENTITY_QUERY='{
      "query": "query getEntity($name: String!) { entity(name: $name) { name keys } }",
      "variables": { "name": "customers" }
    }'

    curl -X POST "${API_URI}" \
      -H "Content-Type: application/json" \
      -H "X-Honeydew-Client: curl-script" \
      -H "X-Honeydew-Workspace: ${WORKSPACE_NAME}" \
      -H "X-Honeydew-Branch: ${BRANCH_NAME}" \
      -u "${API_KEY}:${API_SECRET}" \
      -d "${ENTITY_QUERY}" \
      --max-time 30


    echo -e "\n\n=== Example 4: Using a mutation to reload all workspaces ==="
    # Example of a GraphQL mutation to reload workspace
    export RELOAD_ALL_WORKSPACES='{
      "query": "mutation ReloadAllWorkspaces { reset_all_workspaces }"
    }'

    curl -X POST "${API_URI}" \
      -H "Content-Type: application/json" \
      -H "X-Honeydew-Client: curl-script" \
      -u "${API_KEY}:${API_SECRET}" \
      -d "${RELOAD_ALL_WORKSPACES}" \
      --max-time 30

    echo -e "\n"
    ```

    The above will produce the following kind of output:

    ```
    === Example 1: Querying workspaces ===
    {
      "data": {
        "workspaces": [
          {
            "name": "tasty_bytes"
          },
          {
            "name": "tpch"
          }
        ]
      }
    }


    === Example 2: Querying entities of a specific workspace ===
    {
      "data": {
        "entities": [
          {
            "name": "customers"
          },
          {
            "name": "order_lines"
          },
          {
            "name": "orders"
          },
          {
            "name": "parts"
          },
        ]
      }
    }


    === Example 3: Querying a specific entity with variables ===
    {
      "data": {
        "entity": {
          "keys": [
            "custkey"
          ],
          "name": "customers"
        }
      }
    }


    === Example 4: Using a mutation to reload all workspaces ===
    {
      "data": {
        "reset_all_workspaces": null
      }
    }
    ```
  </Tab>
</Tabs>

## GraphQL API Reference

### Workspaces and Branches

<AccordionGroup>
  <Accordion title="List Workspaces">
    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Query theme={null}
    query {
      workspaces {
        branch
        dwh_connector_name
        errors {
          description
        }
        git_url
        name
        object_key
      }
    }
    ```
  </Accordion>

  <Accordion title="Create Workspace Branch">
    This mutation creates a new branch in the specified workspace.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Editor or higher

    ```graphql GraphQL Mutation theme={null}
    mutation createWorkspaceBranch($workspace_name: String!, $branch_name: String!) {
        create_workspace_branch(
            workspace_name: $workspace_name
            branch_name: $branch_name
        )
    }
    ```

    ```json Successful Result Example theme={null}
    {
      "data": {
        "create_workspace_branch": null
      }
    }
    ```

    ```json Error Example theme={null}
    {
      "data": {
        "create_workspace_branch": null
      },
      "errors": [
        {
          "locations": [
            {
              "column": 3,
              "line": 2
            }
          ],
          "message": "Workspace \"tpchaa\" is not found",
          "path": [
            "create_workspace_branch"
          ]
        }
      ]
    }
    ```
  </Accordion>

  <Accordion title="Rename Workspace Branch">
    This mutation renames an existing branch.
    The `prod` branch cannot be renamed, the branch being renamed must exist, and the new name
    must not already be taken.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Editor or higher

    **Parameters:**

    * `workspace_name`: The name of the workspace the branch belongs to
    * `old_branch_name`: The current name of the branch
    * `new_branch_name`: The new name for the branch

    ```graphql GraphQL Mutation theme={null}
    mutation renameBranch(
            $workspace_name: String!,
            $old_branch_name: String!,
            $new_branch_name: String!) {
        rename_branch(
            workspace_name: $workspace_name
            old_branch_name: $old_branch_name
            new_branch_name: $new_branch_name
        )
    }
    ```
  </Accordion>

  <Accordion title="Delete Workspace Branch">
    This mutation deletes a branch from the specified workspace.
    The `prod` branch cannot be deleted.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Editor or higher

    **Parameters:**

    * `workspace_name`: The name of the workspace the branch belongs to
    * `branch_name`: The name of the branch to delete

    ```graphql GraphQL Mutation theme={null}
    mutation deleteWorkspaceBranch($workspace_name: String!, $branch_name: String!) {
        delete_workspace_branch(
            workspace_name: $workspace_name
            branch_name: $branch_name
        )
    }
    ```
  </Accordion>

  <Accordion title="Reload Workspace">
    This mutation reloads the specified workspace from Git.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Mutation theme={null}
    mutation reloadWorkspace {
        reset_workspace
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "reset_workspace": null
      }
    }
    ```
  </Accordion>

  <Accordion title="Reload All Workspaces">
    This mutation reloads all workspaces from Git.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Mutation theme={null}
    mutation reloadAllWorkspaces {
        reset_all_workspaces
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "reset_all_workspaces": null
      }
    }
    ```
  </Accordion>

  <Accordion title="Reload Workspace for All Users">
    This mutation reloads a workspace from Git for all users.
    This is useful for ensuring that all users see
    the latest changes in the workspace.

    **Workspace/Branch Headers:** Required

    **Permissions:** Admin

    ```graphql GraphQL Mutation theme={null}
    mutation reloadWorkspaceAllUsers {
        reset_workspace_all_users
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "reset_workspace_all_users": null
      }
    }
    ```
  </Accordion>

  <Accordion title="Reload All Workspaces for All Users">
    This mutation reloads all workspaces from Git
    for all users.
    This is useful for ensuring that all users see
    the latest changes in all workspaces,
    in particular after a workspace was added or deleted.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin

    ```graphql GraphQL Mutation theme={null}
    mutation reloadAllWorkspacesAllUsers {
        reset_all_workspaces_all_users
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "reset_all_workspaces_all_users": null
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Querying Schema

<AccordionGroup>
  <Accordion title="List Entities">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Query theme={null}
    query {
        entities {
            ai_description
            description
            display_name
            error {
                description
            }
            fields {
                description
                display_name
                error {
                    description
                }
                folder
                generated_display_name
                generated_folder_name
                git_url
                hidden
                labels
                metadata {
                    metadata {
                        name
                        value
                    }
                    name
                }
                name
                object_key
                owner
                tags {
                    key
                    value
                    source
                }
                ui_url
                ... on CalcAttribute {
                    owner
                    datatype
                    sql
                    timegrain
                }
                ... on DataSet {
                    dataset_type
                    owner
                    sql
                }
                ... on DataSetAttribute {
                    column
                    dataset
                    datatype
                    timegrain
                }
                ... on Metric {
                    datatype
                    owner
                    rollup
                    sql
                }
            }
            generated_display_name
            git_url
            hidden
            is_time_spine
            keys
            labels
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            name
            object_key
            owner
            relations {
                connection {
                    src_field
                    target_field
                }
                connection_expr {
                    sql
                }
                cross_filtering
                rel_join_type
                rel_type
                target_entity
            }
            tags {
                key
                value
                source
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Entity By Name">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `entity_name`: The name of the entity to retrieve

    ```graphql GraphQL Query theme={null}
    query getEntityByName($entity_name: String!) {
        entity(name: $entity_name) {
            ai_description
            description
            display_name
            error {
                description
            }
            generated_display_name
            git_url
            hidden
            is_time_spine
            keys
            labels
            metadata {
    			metadata {
        			name
    				value
              	}
                name
            }
            name
            object_key
            owner
            relations {
                connection {
                    src_field
                    target_field
                }
                connection_expr {
                    sql
                }
                cross_filtering
                rel_join_type
                rel_type
                target_entity
            }
            tags {
                key
                value
                source
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Entity Field By Name">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `entity_name`: The name of the entity to retrieve the field from
    * `name`: The name of the field to retrieve

    ```graphql GraphQL Query theme={null}
    query getEntityFieldByName($entity_name: String!, $name: String!) {
        field(entity_name: $entity_name, name: $name) {
            description
            display_name
            error {
                description
            }
            folder
            generated_display_name
            generated_folder_name
            git_url
            hidden
            labels
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            name
            object_key
            owner
            tags {
                key
                value
                source
            }
            ui_url
            ... on CalcAttribute {
                owner
                datatype
                sql
            }
            ... on DataSet {
              	dataset_type
              	owner
                sql
            }
            ... on DataSetAttribute {
              	column
              	dataset
                datatype
            }
            ... on Metric {
                datatype
                owner
                sql
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="List Domains">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Query theme={null}
    query {
        domains {
            ai_description
            description
            display_name
            error {
                description
            }
            filters {
                name
                sql
            }
            generated_display_name
            git_url
            hidden
            labels
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            name
            object_key
            owner
            parameters {
                description
                name
                value
            }
            source_filters {
                name
                sql
            }
            tags {
                key
                value
                source
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Domain By Name">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `name`: The name of the domain to retrieve

    ```graphql GraphQL Query theme={null}
    query getDomainByName($domain_name: String!) {
        domain(name: $domain_name) {
            ai_description
            description
            display_name
            error {
                description
            }
            filters {
                name
                sql
            }
            generated_display_name
            git_url
            hidden
            labels
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            name
            object_key
            owner
            parameters {
                description
                name
                value
            }
            source_filters {
                name
                sql
            }
            tags {
                key
                value
                source
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="List Global Parameters">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Query theme={null}
    query {
        parameters {
            description
            display_name
            error {
                description
            }
            generated_display_name
            git_url
            hidden
            labels
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            name
            object_key
            owner
            tags {
                key
                value
                source
            }
            value
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Global Parameter By Name">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `name`: The name of the global parameter to retrieve

    ```graphql GraphQL Query theme={null}
    query getGlobalParameterByName($parameter_name: String!) {
        parameter(name: $parameter_name) {
            description
            display_name
            error {
                description
            }
            generated_display_name
            git_url
            hidden
            labels
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            name
            object_key
            owner
            tags {
                key
                value
                source
            }
            value
        }
    }
    ```
  </Accordion>

  <Accordion title="List Dynamic Datasets">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    ```graphql GraphQL Query theme={null}
    query {
        dynamic_datasets {
            ai_description
            attributes
            description
            display_name
            error {
                description
            }
            filters
            generated_display_name
            git_url
            hidden
            labels
            limit
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            metrics
            name
            object_key
            offset
            order {
                alias
                nulls_first
                order
                position
            }
            owner
            parameters {
                description
                name
                value
            }
            tags {
                key
                value
                source
            }
            transform_sql
            use_cache
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Dynamic Dataset By Name">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `name`: The name of the dynamic dataset to retrieve

    ```graphql GraphQL Query theme={null}
    query getDynamicDatasetByName($dynamic_dataset_name: String!) {
        dynamic_dataset(name: $dynamic_dataset_name) {
            ai_description
            attributes
            description
            display_name
            error {
                description
            }
            filters
            generated_display_name
            git_url
            hidden
            labels
            limit
            metadata {
                metadata {
                    name
                    value
                }
                name
            }
            metrics
            name
            object_key
            offset
            order {
                alias
                nulls_first
                order
                position
            }
            owner
            parameters {
                description
                name
                value
            }
            tags {
                key
                value
                source
            }
            transform_sql
            use_cache
        }
    }
    ```
  </Accordion>

  <Accordion title="Search the Semantic Model">
    Finds entities, fields, dynamic datasets, domains, and global parameters whose name, display
    name, or label matches a term.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `what`: The term to search for. Matching is case-insensitive.
    * `search_mode`: `OR` splits the term on whitespace and returns objects matching any word.
      `AND` splits it the same way and returns only objects matching every word. `EXACT` keeps the
      term whole and returns objects whose name, display name, or label equals it.

    Use `entity.field` to search only the fields of matching entities. In `OR` and `AND` modes,
    `customers.` returns every field of the entities matching `customers`.

    **Return Value:**
    Returns every matching object, with no relevance ordering and no result limit. A match can be of
    any model object type, so select through an inline fragment on each type you accept, and select
    `__typename` to see which one matched. `... on Field` covers attributes, metrics, filters, and
    datasets; a dynamic dataset comes back as `Perspective`.

    ```graphql GraphQL Query theme={null}
    query searchModel($what: String!, $search_mode: SearchMode!) {
        search(what: $what, search_mode: $search_mode) {
            __typename
            ... on Field {
                name
                display_name
                labels
            }
            ... on Entity {
                name
                display_name
                labels
            }
            ... on Perspective {
                name
                display_name
                labels
            }
            ... on Domain {
                name
                display_name
                labels
            }
            ... on GlobalParameter {
                name
                display_name
                labels
            }
        }
    }
    ```
  </Accordion>
</AccordionGroup>

### Validate a Workspace

Use these queries to validate a workspace in a CI/CD pipeline: reload the branch from Git,
confirm the workspace loads, then check every object for errors. This is the same sequence
run by the [GitHub Action](/docs/governance/ci-cd/github-actions); see
[CI/CD Overview](/docs/governance/ci-cd/overview) for using it with other CI/CD systems.

All calls below require the `X-Honeydew-Workspace` and `X-Honeydew-Branch` headers, and the
**Viewer** role or higher. A pipeline runs them in the order shown and fails the step if any
error is found.

**1. Reload the branch from Git** so validation reflects the latest commit of the branch:

```graphql GraphQL Mutation theme={null}
mutation { reset_workspace }
```

**2. Check the workspace for load errors.** If the workspace fails to load (for example, a
YAML parse error), the per-object checks are not meaningful — report these errors first:

```graphql GraphQL Query theme={null}
query {
    workspaces {
        name
        branch
        errors {
            description
        }
    }
}
```

**3. Check objects for validation errors.** This query returns every entity, so that
field-level errors surface even inside otherwise-valid entities. Domains, perspectives, and
parameters use the `has_errors: true` argument, so they return only the objects that fail
validation. Treat the workspace as failing if any entity has a non-null `error`, any entity
returns `fields`, or any domain, perspective, or parameter is returned:

```graphql GraphQL Query theme={null}
query {
    entities {
        name
        error {
            description
        }
        fields(has_errors: true) {
            name
            error {
                description
            }
        }
    }
    domains(has_errors: true) {
        name
        error {
            description
        }
    }
    perspectives(has_errors: true) {
        name
        error {
            description
        }
    }
    parameters(has_errors: true) {
        name
        error {
            description
        }
    }
}
```

**4. Check context items and agents.** Context items (instructions and memories) and agents
report errors under `validation_errors`; both use `has_errors: true`, so any returned object
is a failure:

```graphql GraphQL Query theme={null}
query {
    context_items(has_errors: true) {
        __typename
        ... on InstructionFrontmatter {
            path
            validation_errors {
                error
            }
        }
        ... on MemoryFrontmatter {
            path
            validation_errors {
                error
            }
        }
    }
    agents(has_errors: true) {
        path
        validation_errors {
            error
        }
    }
}
```

### Modifying Schema

Except for `validate_object`, which only reads, these mutations run on a
[branch](/docs/governance/git-version-control) only — calling them with `prod` as the branch header
fails. Commit the branch and merge it to publish the change.

<AccordionGroup>
  <Accordion title="Create Object">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `yaml_text`: The YAML definition of the object to create.
      See references for YAML schema [here](/docs/yaml-schema).
    * `force_with_error`:
      * If **false**, the mutation will fail if the deletion causes the workspace to become invalid
        (For example, if the object is used by another object in the workspace).
      * If **true**, the mutation will delete the object even if it causes the workspace to become invalid.
        This is useful if you are performing a set of changes that will eventually make the workspace valid again.

    **Return Value:**

    The mutation returns the created object, or an error if the creation failed.
    You can use qualifiers to get specific fields of the object, such as `name`, `error`, etc.

    ```graphql GraphQL Mutation theme={null}
    mutation createFromYAML($yaml_text: String!, $force_with_error: Boolean!) {
        create_object(yaml_text: $yaml_text, force_with_error: $force_with_error) {
            ... on Field {
                name
                error {
                    description
                }
            }
            ... on Entity {
                name
                error {
                    description
                }
            }
            ... on Perspective {
                name
                error {
                    description
                }
            }
            ... on GlobalParameter {
                name
                error {
                    description
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Update Object">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `yaml_text`: The YAML definition of the object to update.
      See references for YAML schema [here](/docs/yaml-schema).
    * `object_key`: The key of the object to update. This is the `object_key` field that can be retrieved in any query on objects.
    * `force_with_error`:
      * If **false**, the mutation will fail if the deletion causes the workspace to become invalid
        (For example, if the object is used by another object in the workspace).
      * If **true**, the mutation will delete the object even if it causes the workspace to become invalid.
        This is useful if you are performing a set of changes that will eventually make the workspace valid again.

    **Return Value:**

    The mutation returns the updated object, or an error if the update failed.
    You can use qualifiers to get specific fields of the object, such as `name`, `error`, etc.

    ```graphql GraphQL Mutation theme={null}
    mutation updateFromYAML($yaml_text: String!, $object_key: String!, $force_with_error: Boolean!) {
        update_object(
                yaml_text: $yaml_text,
                object_key: $object_key,
                force_with_error: $force_with_error) {
            ... on Field {
                name
                error {
                    description
                }
            }
            ... on Entity {
                name
                error {
                    description
                }
            }
            ... on Perspective {
                name
                error {
                    description
                }
            }
            ... on Domain {
                name
                error {
                    description
                }
            }
            ... on GlobalParameter {
                name
                error {
                    description
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Delete Object">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `object_key`: The key of the object to delete.
    * `force_with_error`:
      * If **false**, the mutation will fail if the deletion causes the workspace to become invalid
        (For example, if the object is used by another object in the workspace).
      * If **true**, the mutation will delete the object even if it causes the workspace to become invalid.
        This is useful if you are performing a set of changes that will eventually make the workspace valid again.

    ```graphql GraphQL Mutation theme={null}
    mutation deleteObject($object_key: String, $force_with_error: Boolean!) {
        delete_object(object_key: $object_key, force_with_error: $force_with_error)
    }
    ```
  </Accordion>

  <Accordion title="Create Entity">
    Creates an [entity](/docs/modeling/entities) together with the dataset that backs it, in one
    call. Use `create_object` instead to add a single object to an existing entity.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `entity_yaml_text`: The YAML definition of the entity. See the [YAML schema](/docs/yaml-schema).
    * `dataset_yaml_text`: The YAML definition of the dataset. Its `entity` field must name the
      entity defined in `entity_yaml_text`, otherwise the mutation fails.

    **Return Value:**
    Returns the created entity. The entity is created even when its definition is invalid, so read
    `error` on the result to see whether it is valid — there is no `force_with_error` to opt out of
    that, unlike `create_object`.

    ```graphql GraphQL Mutation theme={null}
    mutation createEntity($entity_yaml_text: String!, $dataset_yaml_text: String!) {
        create_entity(
                entity_yaml_text: $entity_yaml_text,
                dataset_yaml_text: $dataset_yaml_text) {
            name
            error {
                description
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Validate Object">
    Parses and validates a YAML definition without writing it to the workspace — the dry run for
    `create_object` and `update_object`. It works on any branch.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `yaml_text`: The YAML definition to validate. See the [YAML schema](/docs/yaml-schema).
    * `object_key`: The key of the object being edited. Pass it when validating a change to an
      existing object, and omit it when validating a new one.

    **Return Value:**
    Returns the parsed object, with `error` populated when the definition is invalid.
    The object type depends on the `type` in the YAML, so select through an inline fragment on each
    type you accept, and select `__typename` to see which one came back.
    For a domain, the result is the resolved form with `extends` applied, so it shows what the
    domain would actually expose — `create_object` and `update_object` instead return the
    unresolved definition as submitted.

    ```graphql GraphQL Mutation theme={null}
    mutation validateObject($yaml_text: String!, $object_key: String) {
        validate_object(yaml_text: $yaml_text, object_key: $object_key) {
            __typename
            ... on CalcAttribute {
                name
                error {
                    description
                }
            }
            ... on DataSet {
                name
                error {
                    description
                }
            }
            ... on Domain {
                name
                error {
                    description
                }
            }
            ... on Entity {
                name
                error {
                    description
                }
            }
            ... on Filter {
                name
                error {
                    description
                }
            }
            ... on GlobalParameter {
                name
                error {
                    description
                }
            }
            ... on Metric {
                name
                error {
                    description
                }
            }
            ... on Perspective {
                name
                error {
                    description
                }
            }
        }
    }
    ```
  </Accordion>
</AccordionGroup>

### Agents and Context Items

Manage [agents](/docs/integration/context-layer/agents) and
[context items](/docs/integration/context-layer/context-management) — instructions and memories.

Each object is a Markdown document with YAML frontmatter, passed as the `frontmatter_text`
argument in the format described in the
[agent](/docs/integration/context-layer/agents#yaml-schema) and
[context item](/docs/integration/context-layer/context-management#yaml-schema) schemas.
Honeydew derives the file path from the definition, so no path is passed.

The mutations run on a [branch](/docs/governance/git-version-control) only — calling them with
`prod` as the branch header fails. Commit the branch and merge it to publish the change.
The queries read the branch named in the headers, `prod` included.

<AccordionGroup>
  <Accordion title="Create Agent">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `frontmatter_text`: The agent definition, as Markdown with YAML frontmatter
    * `force_with_error`: If **false** or omitted, the mutation fails when the definition has
      validation errors. If **true**, the agent is written anyway.

    **Return Value:**

    * `object_key`: The key of the created agent, to pass to `update_agent` or `delete_agent`
    * `path`: The path of the file in Git
    * `validation_errors`: Any validation errors on the definition, each with an `error` message
      and the `path` within the definition that caused it
    * `agent`: The parsed agent, including its `name`

    ```graphql GraphQL Mutation theme={null}
    mutation createAgent($frontmatter_text: Frontmatter!, $force_with_error: Boolean) {
        create_agent(
                frontmatter_text: $frontmatter_text,
                force_with_error: $force_with_error) {
            object_key
            path
            validation_errors {
                error
                path
            }
            agent {
                name
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Update Agent">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `object_key`: The key of the agent to update
    * `frontmatter_text`: The new agent definition, as Markdown with YAML frontmatter
    * `force_with_error`: If **false** or omitted, the mutation fails when the definition has
      validation errors. If **true**, the agent is written anyway.

    **Return Value:**

    * `object_key`: The key of the agent
    * `path`: The path of the file in Git
    * `validation_errors`: Any validation errors on the definition
    * `agent`: The parsed agent, including its `name`

    ```graphql GraphQL Mutation theme={null}
    mutation updateAgent(
            $object_key: String!,
            $frontmatter_text: Frontmatter!,
            $force_with_error: Boolean) {
        update_agent(
                object_key: $object_key,
                frontmatter_text: $frontmatter_text,
                force_with_error: $force_with_error) {
            object_key
            path
            validation_errors {
                error
                path
            }
            agent {
                name
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Delete Agent">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `object_key`: The key of the agent to delete

    **Return Value:**
    Returns the name of the deleted agent.
    The mutation fails if no agent has that key.

    ```graphql GraphQL Mutation theme={null}
    mutation deleteAgent($object_key: String!) {
        delete_agent(object_key: $object_key)
    }
    ```
  </Accordion>

  <Accordion title="Create Context Item">
    Creates an instruction or a memory, according to the `type` and `subtype` in the definition.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `frontmatter_text`: The context item definition, as Markdown with YAML frontmatter
    * `force_with_error`: If **false** or omitted, the mutation fails when the definition has
      validation errors. If **true**, the context item is written anyway.

    **Return Value:**
    Returns `InstructionFrontmatter` or `MemoryFrontmatter`, so select the fields through an inline
    fragment on each, and select `__typename` to see which of the two was created. Both provide:

    * `object_key`: The key of the created context item, to pass to `update_context_object` or
      `delete_context_object`
    * `path`: The path of the file in Git
    * `validation_errors`: Any validation errors on the definition

    ```graphql GraphQL Mutation theme={null}
    mutation createContextObject($frontmatter_text: Frontmatter!, $force_with_error: Boolean) {
        create_context_object(
                frontmatter_text: $frontmatter_text,
                force_with_error: $force_with_error) {
            __typename
            ... on InstructionFrontmatter {
                object_key
                path
                validation_errors {
                    error
                    path
                }
            }
            ... on MemoryFrontmatter {
                object_key
                path
                validation_errors {
                    error
                    path
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Update Context Item">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `object_key`: The key of the context item to update
    * `frontmatter_text`: The new context item definition, as Markdown with YAML frontmatter
    * `force_with_error`: If **false** or omitted, the mutation fails when the definition has
      validation errors. If **true**, the context item is written anyway.

    **Return Value:**
    Returns `InstructionFrontmatter` or `MemoryFrontmatter`, as for `create_context_object`.

    ```graphql GraphQL Mutation theme={null}
    mutation updateContextObject(
            $object_key: String!,
            $frontmatter_text: Frontmatter!,
            $force_with_error: Boolean) {
        update_context_object(
                object_key: $object_key,
                frontmatter_text: $frontmatter_text,
                force_with_error: $force_with_error) {
            __typename
            ... on InstructionFrontmatter {
                object_key
                path
                validation_errors {
                    error
                    path
                }
            }
            ... on MemoryFrontmatter {
                object_key
                path
                validation_errors {
                    error
                    path
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Delete Context Item">
    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `object_key`: The key of the context item to delete

    **Return Value:**
    Returns the name of the deleted context item.
    The mutation fails if no context item has that key.

    ```graphql GraphQL Mutation theme={null}
    mutation deleteContextObject($object_key: String!) {
        delete_context_object(object_key: $object_key)
    }
    ```
  </Accordion>

  <Accordion title="List Agents">
    Lists the agents of the workspace and branch.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `has_errors`: If **true**, returns only the agents whose definition has validation errors. If
      **false**, only those without them. Omit it to return every agent.

    **Return Value:**

    * `object_key`: The key of the agent, to pass to the `agent` query and to the `update_agent`
      and `delete_agent` mutations
    * `path`: The path of the file in Git
    * `ui_url`: The URL of the agent in the Honeydew UI
    * `validation_errors`: Any validation errors on the definition
    * `agent`: The parsed agent, including its `name`, `display_name`, `description` and `domain`

    ```graphql GraphQL Query theme={null}
    query listAgents($has_errors: Boolean) {
        agents(has_errors: $has_errors) {
            object_key
            path
            ui_url
            validation_errors {
                error
                path
            }
            agent {
                name
                display_name
                description
                domain
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Agent">
    Returns one agent by its key. The query fails if no agent has that key.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `object_key`: The key of the agent to read

    **Return Value:**

    * `object_key`: The key of the agent
    * `path`: The path of the file in Git
    * `frontmatter_text`: The agent definition, to edit and pass back to `update_agent`
    * `validation_errors`: Any validation errors on the definition
    * `agent`: The parsed agent, including its `name`, `display_name`, `description`, `domain` and
      `context` — the names and the glob patterns of the context items it loads

    ```graphql GraphQL Query theme={null}
    query getAgent($object_key: String!) {
        agent(object_key: $object_key) {
            object_key
            path
            frontmatter_text
            validation_errors {
                error
                path
            }
            agent {
                name
                display_name
                description
                domain
                context
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Find Agents for a Question">
    Returns the agents whose scope fits a question, to route the question to one of them.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `question`: The question to route

    **Return Value:**
    Returns agents, with the fields listed under `agents`. Agents whose definition has validation
    errors are skipped, and the list is empty when no agent fits the question.

    ```graphql GraphQL Query theme={null}
    query agentsForQuestion($question: String!) {
        agents_for_question(question: $question) {
            object_key
            agent {
                name
                description
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="List Context Items">
    Lists the instructions and the memories of the workspace and branch.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `has_errors`: If **true**, returns only the context items whose definition has validation
      errors. If **false**, only those without them. Omit it to return every context item.
    * `names`: Return only the context items with these names. Names with no context item are
      ignored.

    **Return Value:**
    Returns `InstructionFrontmatter` or `MemoryFrontmatter`, as for `create_context_object`, so
    select the fields through an inline fragment on each:

    * `object_key`: The key of the context item
    * `path`: The path of the file in Git
    * `instruction`: The parsed instruction, including its `name`, `title`, `subtype` and `enabled`
    * `memory`: The parsed memory, with the same fields and the dates it covers

    ```graphql GraphQL Query theme={null}
    query listContextItems($has_errors: Boolean, $names: [String!]) {
        context_items(has_errors: $has_errors, names: $names) {
            __typename
            ... on InstructionFrontmatter {
                object_key
                path
                instruction {
                    name
                    title
                    subtype
                }
            }
            ... on MemoryFrontmatter {
                object_key
                path
                memory {
                    name
                    title
                    subtype
                    from_date
                    to_date
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="List Instructions or Memories">
    `instructions` and `memories` each return one type, so their fields are selected without an
    inline fragment.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `has_errors`: If **true**, returns only the items whose definition has validation errors.
      If **false**, only those without them. Omit it to return every item.
      Neither query takes the `names` argument that `context_items` accepts.

    **Return Value:**
    The fields of `context_items`, on `InstructionFrontmatter` and on `MemoryFrontmatter`.

    ```graphql GraphQL Query theme={null}
    query listInstructionsAndMemories($has_errors: Boolean) {
        instructions(has_errors: $has_errors) {
            object_key
            instruction {
                name
                title
                subtype
            }
        }
        memories(has_errors: $has_errors) {
            object_key
            memory {
                name
                title
                subtype
                from_date
                to_date
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Instruction or Memory">
    Returns one context item by its key. Each query reads its own type: `instruction` fails on the
    key of a memory, and `memory` on the key of an instruction.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `object_key`: The key of the context item to read

    **Return Value:**
    The fields of `context_items` for that type, `frontmatter_text` — the definition to edit and pass
    back to `update_context_object` — and `referencing_agents`, the agents that load the item.

    For an instruction:

    ```graphql GraphQL Query theme={null}
    query getInstruction($object_key: String!) {
        instruction(object_key: $object_key) {
            object_key
            path
            frontmatter_text
            instruction {
                name
                title
                subtype
                enabled
            }
            referencing_agents {
                agent {
                    name
                }
            }
        }
    }
    ```

    And for a memory:

    ```graphql GraphQL Query theme={null}
    query getMemory($object_key: String!) {
        memory(object_key: $object_key) {
            object_key
            path
            frontmatter_text
            memory {
                name
                title
                subtype
                enabled
                from_date
                to_date
            }
            referencing_agents {
                agent {
                    name
                }
            }
        }
    }
    ```
  </Accordion>
</AccordionGroup>

### Data Warehouse

Browse the data warehouse of a workspace, describe a custom SQL query, and import tables as
entities. AI agents reach most of these operations through the [MCP server](/docs/integration/mcp).

The queries below run against the warehouse connection of the workspace and branch in the
headers. `get_databases` and `get_schemas` also accept `connector_name`, to browse an
organization-level connector instead, together with the `dwh_type` it is configured for:
`snowflake`, `databricks` or `bigquery`. The other queries always use the workspace connection.

#### Browsing Metadata

<AccordionGroup>
  <Accordion title="List Databases">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `dwh_type`: The type of the warehouse to browse, as a `DwhType` value.
      Required with `connector_name`.
    * `connector_name`: Browse this organization-level connector instead of the workspace
      connection. Requires the Admin role.

    **Return Value:**
    Returns the names of the databases the connection can reach.

    ```graphql GraphQL Query theme={null}
    query listDatabases($dwh_type: DwhType, $connector_name: String) {
        get_databases(dwh_type: $dwh_type, connector_name: $connector_name)
    }
    ```
  </Accordion>

  <Accordion title="List Schemas">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `database`: The database to list the schemas of.
      Omit it to list the schemas of every database, each returned as `database.schema`.
    * `dwh_type`: The type of the warehouse to browse, as a `DwhType` value.
      Required with `connector_name`.
    * `connector_name`: Browse this organization-level connector instead of the workspace
      connection. Requires the Admin role.

    **Return Value:**
    Returns the names of the schemas.

    ```graphql GraphQL Query theme={null}
    query listSchemas(
            $database: String,
            $dwh_type: DwhType,
            $connector_name: String) {
        get_schemas(
            database: $database
            dwh_type: $dwh_type
            connector_name: $connector_name
        )
    }
    ```
  </Accordion>

  <Accordion title="List Tables">
    Lists the tables, external tables and views of a schema.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `database`: The database to list tables in. Omit it to use the database of the connection.
    * `schema`: Return only the tables of this schema

    **Return Value:**

    * `table_catalog`: The database of the table
    * `table_schema`: The schema of the table
    * `table_name`: The name of the table
    * `table_type`: `BASE_TABLE`, `EXTERNAL_TABLE` or `VIEW`
    * `row_count`: The number of rows, when the warehouse reports it
    * `data_bytes`: The size in bytes, when the warehouse reports it
    * `created`: When the table was created
    * `last_altered`: When the table was last changed
    * `comment`: The comment on the table
    * `tags`: The warehouse tags on the table, each with its `key` and `value`

    ```graphql GraphQL Query theme={null}
    query listTables($database: String, $schema: String) {
        get_tables(database: $database, schema: $schema) {
            table_catalog
            table_schema
            table_name
            table_type
            row_count
            data_bytes
            created
            last_altered
            comment
            tags {
                key
                value
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Table Info">
    Returns the metadata of a single table together with its columns, to decide what to import.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `database`: The database of the table
    * `schema`: The schema of the table
    * `table`: The name of the table

    **Return Value:**

    * `table_md`: The metadata of the table, with the same fields `get_tables` returns
    * `columns_md`: One entry per column, each with its `column_name`, `ordinal_position`,
      `data_type`, `is_nullable`, `comment`, `tags`, `is_primary_key` and `foreign_keys`.
      Each foreign key names the table and column it points at.

    ```graphql GraphQL Query theme={null}
    query getTableInfo($database: String!, $schema: String!, $table: String!) {
        get_table_info(database: $database, schema: $schema, table: $table) {
            table_md {
                table_catalog
                table_schema
                table_name
                table_type
                row_count
                data_bytes
                created
                last_altered
                comment
                tags {
                    key
                    value
                }
            }
            columns_md {
                column_name
                ordinal_position
                data_type
                is_nullable
                comment
                is_primary_key
                tags {
                    key
                    value
                }
                foreign_keys {
                    table_catalog
                    table_schema
                    table_name
                    column_name
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="List Semantic Views">
    Lists the Snowflake semantic views of a schema. Snowflake only.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `database`: The database to list semantic views in.
      Omit it to use the database of the connection.
    * `schema`: Return only the semantic views of this schema

    **Return Value:**

    * `semantic_view_catalog`: The database of the semantic view
    * `semantic_view_schema`: The schema of the semantic view
    * `semantic_view_name`: The name of the semantic view
    * `comment`: The comment on the semantic view

    ```graphql GraphQL Query theme={null}
    query listSemanticViews($database: String, $schema: String) {
        get_semantic_views(database: $database, schema: $schema) {
            semantic_view_catalog
            semantic_view_schema
            semantic_view_name
            comment
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Custom SQL Info">
    Returns the columns a SQL query produces, to define a dataset from
    [custom SQL](/docs/modeling/source-data#custom-sql). Honeydew describes the query on the warehouse
    rather than running it.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `sql`: The query to describe. It resolves against the current
      [environment](/docs/governance/environments), and may use [parameters](/docs/parameters).

    **Return Value:**

    * `columns_md`: One entry per column of the query result, each with its `column_name`,
      `ordinal_position`, `data_type`, `is_nullable` and `comment`

    ```graphql GraphQL Query theme={null}
    query getCustomSqlInfo($sql: String!) {
        get_custom_sql_info(sql: $sql) {
            columns_md {
                column_name
                ordinal_position
                data_type
                is_nullable
                comment
            }
        }
    }
    ```
  </Accordion>
</AccordionGroup>

#### Importing and Syncing Tables

These mutations change the model of the branch in the headers, and commit the change to Git. Run
them on a development branch, then
[merge the change](/docs/governance/git-version-control#branch-based-development).

<AccordionGroup>
  <Accordion title="Import Tables">
    Imports tables from the warehouse into the model. Each table becomes an entity, with its
    columns as attributes. It cannot run on the `prod` branch of a workspace.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `tables`: The tables to import, as fully qualified `database.schema.table` names
    * `check_keys_uniqueness`: Query the warehouse to confirm that detected entity keys are
      unique before applying them. Defaults to `false`.

    **Return Value:**

    * `detect_entities_relations_result`: The result of the
      [relation detection](/docs/modeling/relations) that follows the import, with its `response` text

    If a table fails to import, the mutation returns an error listing the failures.
    The tables that did import are kept.

    ```graphql GraphQL Mutation theme={null}
    mutation importTablesFromDwh(
            $tables: [String!]!,
            $check_keys_uniqueness: Boolean) {
        import_tables_from_dwh(
                tables: $tables,
                check_keys_uniqueness: $check_keys_uniqueness) {
            detect_entities_relations_result {
                response
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Sync Table from Source">
    Syncs one entity's table from its source, refreshing the metadata in the semantic layer.
    Use it after the source table changes in the warehouse.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `dataset_object_key`: The object key of the dataset to sync. You can find dataset object keys
      by querying the entity's fields and looking for the `DataSet` type's `object_key` field.
    * `force_with_error`: Apply the change even when it leaves the workspace invalid, for example
      when a removed column is still used elsewhere. Defaults to `false`.

    ```graphql GraphQL Mutation theme={null}
    mutation syncTableFromSource(
            $dataset_object_key: String!,
            $force_with_error: Boolean) {
        sync_table_from_source(
            dataset_object_key: $dataset_object_key
            force_with_error: $force_with_error
        )
    }
    ```
  </Accordion>

  <Accordion title="Sync All Tables from Source">
    Syncs every table of the workspace from its source, refreshing the metadata in the semantic
    layer.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `force_with_error`: Apply the changes even when they leave the workspace invalid.
      Defaults to `false`.

    ```graphql GraphQL Mutation theme={null}
    mutation syncAllTablesFromSource($force_with_error: Boolean) {
        sync_all_tables_from_source(force_with_error: $force_with_error)
    }
    ```
  </Accordion>
</AccordionGroup>

### Deployment

<AccordionGroup>
  <Accordion title="Deploy Dynamic Dataset">
    Deploy a [dynamic dataset](/docs/dynamic-datasets) according to its deployment settings.
    Use this for [aggregate aware caching](/docs/performance/aggregate-awareness) and
    [incremental aggregate updates](/docs/performance/aggregate-incremental-updates).

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `perspective_name`: The name of the dynamic dataset to deploy

    **Return Value:**
    Returns the SQL query used to select from the deployed dynamic dataset.

    ```graphql GraphQL Mutation theme={null}
    mutation deployDynamicDataset($perspective_name: String!) {
        deploy_perspective(perspective_name: $perspective_name)
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "deploy_perspective": "SELECT ... FROM db.schema.my_dataset"
      }
    }
    ```
  </Accordion>

  <Accordion title="Deploy Entity">
    Deploy an entity according to its deployment settings,
    to update the [entity cache](/docs/performance/entity-caching).

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `entity_name`: The name of the entity to deploy

    **Return Value:**
    Returns the SQL query used to select from the deployed entity cache.

    ```graphql GraphQL Mutation theme={null}
    mutation deployEntity($entity_name: String!) {
        deploy_entity(entity_name: $entity_name)
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "deploy_entity": "SELECT ... FROM db.schema.my_entity_cache"
      }
    }
    ```
  </Accordion>

  <Accordion title="Refresh Dynamic Dataset Data">
    Refresh the data for a dynamic dataset that has already been deployed.

    * For **views**: no-op, returns `false`.
    * For **tables**: redeploys the table with fresh data, returns `true`.
    * For **dynamic tables** (Snowflake only): triggers an incremental refresh, returns `true`.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `perspective_name`: The name of the dynamic dataset to refresh

    **Return Value:**
    Returns `true` if data was refreshed, `false` if no refresh was needed.

    ```graphql GraphQL Mutation theme={null}
    mutation refreshDynamicDataset($perspective_name: String!) {
        refresh_data_for_perspective(perspective_name: $perspective_name)
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "refresh_data_for_perspective": true
      }
    }
    ```
  </Accordion>

  <Accordion title="Clear Deployed Cache Status">
    Clears the deployed cache status so Honeydew re-evaluates cache validity on the next query.

    <Note>
      Honeydew scans the data warehouse information schema to check the
      validity of caches. If an entity or dynamic dataset used for caching
      was rebuilt or replaced outside Honeydew
      (for example, via a third-party tool), call this mutation to notify
      Honeydew that the cache was updated.
    </Note>

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Return Value:**
    Returns `null`.

    ```graphql GraphQL Mutation theme={null}
    mutation clearDeployedCacheStatus {
        clear_deployed_cache_status
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "clear_deployed_cache_status": null
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Queries

<AccordionGroup>
  <Accordion title="Get SQL for adhoc query">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `yaml_text`: YAML definition of a dynamic dataset, which represents a query.
      For more information on the YAML format, see [Dynamic Dataset YAML](/docs/dynamic-datasets#yaml-schema).

      Here's an example of a simple dynamic dataset YAML:

      ```yaml theme={null}
      type: perspective
      name: sample_query
      domain: sales
      attributes:
        - customers.customer_id
      metrics:
        - orders.total_sales
      filters:
        - orders.order_date >= '2025-01-01'
      ```

    **Return Value:**

    * `domain`: The domain the query was built in, as named in the YAML.
    * `dwh_role`: The role to use for the query, from the workspace, branch and domain
      [connection settings](/docs/governance/environments). `null` when not set.
    * `dwh_warehouse`: The warehouse to use for the query, from the workspace, branch and domain
      [connection settings](/docs/governance/environments). `null` when not set.
    * `sql`: A list of the actual data warehouse SQL queries to run, translated from the provided
      YAML by the Honeydew semantic layer.
      Note that there can be multiple sql statements to run, for example - there might be a `SET` statement to set values for parameters used in the sql query.

    ```graphql GraphQL Query theme={null}
    query getSqlFromYaml($yaml_text: String!) {
        get_sql_from_yaml(yaml_text: $yaml_text) {
            domain
            dwh_role
            dwh_warehouse
            sql
        }
    }
    ```
  </Accordion>

  <Accordion title="Translate SQL interface query to data warehouse SQL">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `sql`: The SQL query to translate. This should be a valid SQL query in **Trino** dialect.
      For more information, see [SQL Interface](/docs/integration/sql-interface) documentation.

      Here's an example of a simple sql query to translate:

      ```sql theme={null}
        SELECT
            "customers.customer_id",
            AGG("orders.total_sales")
        FROM "domains"."sales"
        WHERE "orders.order_date" >= '2025-01-01'
        GROUP BY 1
        ORDER BY 1 DESC
        LIMIT 30
      ```

    **Return Value:**

    * `domain`: The domain to use for the query (if applicable), as extracted from the SQL query.
    * `dwh_role`: The role to use for the query, from the workspace, branch and domain
      [connection settings](/docs/governance/environments). `null` when not set.
    * `dwh_warehouse`: The warehouse to use for the query, from the workspace, branch and domain
      [connection settings](/docs/governance/environments). `null` when not set.
    * `sql`: A list of the actual data warehouse SQL queries to run, translated from the provided SQL
      query by the Honeydew semantic layer.
      Note that there can be multiple sql statements to run, for example - there might be a `SET` statement to set values for parameters used in the sql query.

    ```graphql GraphQL Query theme={null}
    query adhocSql($sql: String!) {
        adhoc_sql(sql: $sql) {
            domain
            dwh_role
            dwh_warehouse
            sql
        }
    }
    ```
  </Accordion>
</AccordionGroup>

### Query History

Query history holds the queries executed against the semantic model, with the generated SQL
and the execution details. See [Query history](/docs/monitoring/query-history) for what is
recorded and how long records are kept.

You see and cancel your own queries only. Admins see and cancel the queries of all users.

<AccordionGroup>
  <Accordion title="List Query History" id="list-query-history">
    Lists the queries executed in the workspace and branch set in the request headers,
    newest first.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `limit`: Maximum number of queries to return. Defaults to 250.
    * `offset`: Number of queries to skip for pagination
    * `user_in`: Filter by the email of the user who ran the query
    * `status_in`: Filter by status: `RUNNING`, `SUCCESS`, `FAILED` or `CANCELED`
    * `from_execution_time`: Return queries that started at or after this time
    * `to_execution_time`: Return queries that started at or before this time
    * `client_in`: Filter by the client identifier set in the `X-Honeydew-Client` header
    * `domain_in`: Filter by domain name. Include `null` in the list to also match queries
      that ran without a domain.

    **Return Value:**

    A list of `PerspectiveQuery` objects. Each object includes:

    * `name`: The ID of the query
    * `object_key`: The key of the query record, to pass to `cancel_query`
    * `domain`: The domain the query targeted
    * `owner`: Email of the user who ran the query
    * `dwh_query_id`: The query ID assigned by the data warehouse
    * `sql`: The generated data warehouse SQL
    * `yaml`: The semantic definition of the query, as attributes, metrics and filters
    * `last_update_time`: When the query record was last written
    * `error`: `description` holds the error message when the query failed
    * `ui_url`: The URL of the query in the Honeydew UI
    * `metadata`: Sections of `name`/`value` pairs. The `honeydew` section holds the
      execution details:

      * `dwh_status`: The status of the query. Absent when the query was compiled but
        never run on the data warehouse.
      * `client_name`: The client that submitted the query
      * `compile_start_timestamp`: When Honeydew started compiling the query
      * `compile_end_timestamp`: When Honeydew finished compiling the query
      * `dwh_end_timestamp`: When the data warehouse finished running the generated SQL
      * `original_sql`: The source query, for queries submitted as SQL, MDX or text
      * `aggregates_used`: The [aggregates](/docs/performance/aggregate-awareness) the query
        was accelerated with

      Compilation time, runtime and total duration are derived, not stored — respectively
      `compile_end_timestamp` minus `compile_start_timestamp`, `dwh_end_timestamp` minus
      `compile_end_timestamp`, and `dwh_end_timestamp` minus `compile_start_timestamp`.

    ```graphql GraphQL Query theme={null}
    query listQueryHistory(
            $limit: NonNegativeInt,
            $offset: NonNegativeInt,
            $user_in: [String],
            $status_in: [DwhQueryStatus],
            $from_execution_time: Datetime,
            $to_execution_time: Datetime,
            $client_in: [String!],
            $domain_in: [String]) {
        perspective_queries(
            limit: $limit,
            offset: $offset,
            user_in: $user_in,
            status_in: $status_in,
            from_execution_time: $from_execution_time,
            to_execution_time: $to_execution_time,
            client_in: $client_in,
            domain_in: $domain_in) {
            name
            object_key
            domain
            owner
            dwh_query_id
            sql
            yaml
            last_update_time
            error {
                description
            }
            ui_url
            metadata {
                name
                metadata {
                    name
                    value
                }
            }
        }
    }
    ```

    ```json Example: Failed Power BI queries in a time range theme={null}
    {
      "limit": 100,
      "offset": 0,
      "status_in": ["FAILED"],
      "client_in": ["Power BI"],
      "from_execution_time": "2025-01-01T00:00:00Z",
      "to_execution_time": "2025-12-31T23:59:59Z"
    }
    ```
  </Accordion>

  <Accordion title="Get Query By ID">
    Returns a single query record by ID. Fetching a query that does not exist, or one you
    cannot see, returns an error.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `name`: The ID of the query, as returned by [listing query history](#list-query-history)

    **Return Value:**

    A `PerspectiveQuery` object, with the fields listed under
    [List Query History](#list-query-history).

    ```graphql GraphQL Query theme={null}
    query getQuery($name: String!) {
        perspective_query(name: $name) {
            name
            object_key
            domain
            owner
            dwh_query_id
            sql
            yaml
            last_update_time
            error {
                description
            }
            ui_url
            metadata {
                name
                metadata {
                    name
                    value
                }
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Query Results">
    Returns the rows a query in history produced, read back from its run on the data warehouse.
    A query that was compiled but never ran on the data warehouse is run now.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `name`: The ID of the query, as returned by [listing query history](#list-query-history)
    * `limit`: Maximum number of rows to return. Defaults to 10.
    * `offset`: Number of rows to skip for pagination
    * `sort_fields`: How to sort the rows. Each entry sets `order` (`ASC` or `DESC`),
      `nulls_first`, and exactly one of `alias` (the column name) or `position`
      (the column position).

    **Return Value:**

    * `columns`: The result columns, each with a `name` and a `type`
    * `data`: The result rows, each holding its `values` in column order
    * `sql`: The generated data warehouse SQL of the query

    ```graphql GraphQL Query theme={null}
    query getQueryResults(
            $name: String!,
            $limit: NonNegativeInt,
            $offset: NonNegativeInt,
            $sort_fields: [SortFieldInput!]) {
        perspective_query_results(
            name: $name,
            limit: $limit,
            offset: $offset,
            sort_fields: $sort_fields) {
            columns {
                name
                type
            }
            data {
                values
            }
            sql
        }
    }
    ```
  </Accordion>

  <Accordion title="Cancel Running Query">
    Cancels a query that is still running on the data warehouse. Canceling a query that
    already finished does nothing.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `object_key`: The `object_key` of the query record, as returned by
      [listing query history](#list-query-history)

    **Return Value:**
    Returns `null`.

    ```graphql GraphQL Mutation theme={null}
    mutation cancelQuery($object_key: String!) {
        cancel_query(object_key: $object_key)
    }
    ```
  </Accordion>
</AccordionGroup>

### AI

<AccordionGroup>
  <Accordion title="Create Chat for Deep Analysis Questions">
    This mutation creates a new chat session for deep analysis questions.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `agent`: **Required.** The name of the [agent](/docs/integration/context-layer/agents)
      to run the analysis with. The agent supplies both the domain to query and the
      context items loaded into the session.
    * `show_charts`: Whether the analysis produces charts. Defaults to `true`.

    **Return Value:**

    * `chat_id`: The ID of the created chat session
    * `domain`: The domain the agent is scoped to
    * `ui_url`: The URL of the chat in the Honeydew UI

    ```graphql GraphQL Query theme={null}
    mutation createDeepAnalysisChat(
            $agent: String!, $show_charts: Boolean) {
        create_chat(agent: $agent, show_charts: $show_charts) {
            chat_id
            domain
            ui_url
        }
    }
    ```

    To find the agent name to pass, list the agents in the workspace along with
    the domain each one is built on:

    ```graphql GraphQL Query theme={null}
    query listAgents {
        agents {
            agent {
                name
                display_name
                description
                domain
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Ask Deep Analysis Questions">
    This mutation runs a multi-step agentic analysis
    question, using the semantic layer as the
    source of truth.
    It blocks until the analysis completes
    (up to 5 minutes).

    The response includes markdown text, tabular data,
    and chart visualizations produced during
    the analysis.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `chat_id`: The ID of the conversation
      to use for the question
    * `question`: The deep analysis question
      to ask the AI

    **Return Value:**

    * `response`: A list of content items produced
      by the analysis. Each item is one of:
      * `MarkdownContent`: Textual analysis with
        `text` and `category`
        (`final_conclusion`, `interpretation`,
        `plan`, or `user_response`)
      * `DataContent`: Tabular data with `results`
        (columns and rows)
      * `GraphContent`: A chart. `vega_lite` is a
        complete Vega-Lite specification with the
        data embedded, so it renders as-is.
        `visualization_hint` describes the intended
        chart in natural language, and `group_name`
        groups related content. Also select `data`
        to get the rows on their own, to render with
        a different charting library.
    * `suggested_responses`: A list of suggested
      follow-up questions
    * `ui_url`: The URL of the chat in the
      Honeydew UI

    ```graphql GraphQL Query theme={null}
    mutation askDeepAnalysisQuestion(
            $chat_id: ChatId!,
            $question: String!) {
        ask_deep_analysis_question_sync(
                chat_id: $chat_id,
                question: $question) {
            response {
                ... on MarkdownContent {
                    text
                    category
                }
                ... on DataContent {
                    results {
                        columns { name type }
                        data { values }
                        sql
                    }
                    total_row_count
                    group_name
                }
                ... on GraphContent {
                    vega_lite
                    visualization_hint
                    group_name
                }
            }
            suggested_responses
            ui_url
        }
    }
    ```
  </Accordion>

  <Accordion title="Abort Deep Analysis Chat">
    This mutation aborts a running deep analysis chat,
    stopping any in-progress analysis.
    Use it to cancel a long-running question
    before it completes.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `chat_id`: The ID of the chat session to abort

    **Return Value:**

    * `null` on success
    * `FailureResult` with `error_code` and `message` on error

    ```graphql GraphQL Query theme={null}
    mutation abortDeepAnalysisChat(
            $chat_id: ChatId!) {
        abort_chat(chat_id: $chat_id) {
            error_code
            message
        }
    }
    ```
  </Accordion>

  <Accordion title="List Deep Analysis Chats" id="list-deep-analysis-chats">
    Lists the deep analysis chats of the workspace and branch set in the request headers,
    newest first.

    You see your own chats only. Admins see the chats of all users.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `limit`: Maximum number of chats to return. Omit it to return all chats.
    * `offset`: Number of chats to skip for pagination

    **Return Value:**

    A list of `ChatData` objects. Each object includes:

    * `chat_id`: The ID of the chat, to pass to the other chat calls
    * `chat_title`: The title of the chat
    * `user_feedback`: The feedback stored on the chat, or `null` if there is none
    * `user_display_name`: Display name of the user who owns the chat
    * `creation_time`: When the chat was created
    * `domain`: The domain the chat queries
    * `agent`: The agent the chat runs with
    * `is_readonly`: `true` if the chat can no longer be continued,
      because the workspace changed since the chat was created
    * `is_running`: `true` if an analysis is currently running in the chat
    * `ui_url`: The URL of the chat in the Honeydew UI

    ```graphql GraphQL Query theme={null}
    query listDeepAnalysisChats(
            $limit: NonNegativeInt,
            $offset: NonNegativeInt) {
        get_all_chats(limit: $limit, offset: $offset) {
            chat_id
            chat_title
            user_feedback
            user_display_name
            creation_time
            domain
            agent
            is_readonly
            is_running
            ui_url
        }
    }
    ```
  </Accordion>

  <Accordion title="Get Deep Analysis Chat">
    Returns a single chat by ID. Fetching a chat that does not exist, or one you cannot see,
    returns an error.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `chat_id`: The ID of the chat, as returned by `create_chat` or by
      [listing chats](#list-deep-analysis-chats)

    **Return Value:**

    A `ChatData` object, with the fields listed under
    [List Deep Analysis Chats](#list-deep-analysis-chats).

    ```graphql GraphQL Query theme={null}
    query getDeepAnalysisChat($chat_id: ChatId!) {
        get_chat(chat_id: $chat_id) {
            chat_id
            chat_title
            user_feedback
            user_display_name
            creation_time
            domain
            agent
            is_readonly
            is_running
            ui_url
        }
    }
    ```
  </Accordion>

  <Accordion title="Rename Deep Analysis Chat">
    Sets the title of a chat. You can rename any chat you can see.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `chat_id`: The ID of the chat to rename
    * `new_title`: The new title of the chat

    **Return Value:**

    The updated `ChatData` object, with the fields listed under
    [List Deep Analysis Chats](#list-deep-analysis-chats).

    ```graphql GraphQL Mutation theme={null}
    mutation renameDeepAnalysisChat(
            $chat_id: ChatId!,
            $new_title: String!) {
        rename_chat(chat_id: $chat_id, new_title: $new_title) {
            chat_id
            chat_title
        }
    }
    ```
  </Accordion>

  <Accordion title="Set Deep Analysis Chat Feedback">
    Stores user feedback on a chat, replacing any feedback stored earlier.
    You can set feedback on any chat you can see. The feedback is also
    returned by [AI question history](#list-ai-question-history), which can filter on it.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `chat_id`: The ID of the chat
    * `user_feedback`: The feedback to store. Pass `null` to clear the feedback on the chat.

    **Return Value:**

    The updated `ChatData` object, with the fields listed under
    [List Deep Analysis Chats](#list-deep-analysis-chats).

    ```graphql GraphQL Mutation theme={null}
    mutation updateDeepAnalysisChatFeedback(
            $chat_id: ChatId!,
            $user_feedback: String) {
        update_chat_feedback(
                chat_id: $chat_id,
                user_feedback: $user_feedback) {
            chat_id
            user_feedback
        }
    }
    ```
  </Accordion>

  <Accordion title="Delete Deep Analysis Chat">
    Deletes a chat. It can no longer be listed, fetched or continued.
    Unlike the other chat calls, this one acts on your own chats only — an admin who can see
    another user's chat still cannot delete it.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `chat_id`: The ID of the chat to delete

    **Return Value:**
    Returns `null`.

    ```graphql GraphQL Mutation theme={null}
    mutation deleteDeepAnalysisChat($chat_id: ChatId!) {
        delete_chat(chat_id: $chat_id)
    }
    ```
  </Accordion>

  <Accordion title="List AI Question History" id="list-ai-question-history">
    Returns a paginated list of AI questions asked in the workspace,
    with filtering support.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `limit`: Maximum number of results to return (up to 1000)
    * `offset`: Number of results to skip for pagination
    * `params`: Optional filter parameters:
      * `domain`: Filter by domain name(s)
      * `llm_model`: Filter by LLM model name(s)
      * `conversation_id`: Filter by conversation ID(s)
      * `asked_by`: Filter by user display name(s)
      * `client`: Filter by client name(s)
      * `agent`: Filter by agent name(s)
      * `status`: Filter by response status (`FINISHED` or `FAILED`)
      * `from_execution_time`: Filter by start time (inclusive)
      * `to_execution_time`: Filter by end time (inclusive)
      * `question`: Filter by question text (partial match)
      * `llm_response`: Filter by LLM response text (partial match)
      * `has_feedback`: Filter to questions whose chat has user feedback

    **Return Value:**

    A list of `AnalystResponse` objects. Each object includes:

    * `question_id`: Unique ID for the question
    * `response_type`: `QUICK_ANALYSIS` or `DEEP_ANALYSIS`
    * `question`: The question text
    * `asked_by`: Display name of the user who asked the question
    * `client`: The client identifier set in the `X-Honeydew-Client` header
    * `agent`: The agent that handled the question, if applicable
    * `conversation_id`: The ID of the deep analysis chat this question belongs to
    * `execution_time`: When the question was executed
    * `creation_time`: When the question record was created
    * `llm_model`: The LLM model used to answer the question
    * `status`: `FINISHED` or `FAILED`
    * `sql`: The generated SQL, if applicable
    * `error`: Error message if the question failed
    * `runtime_ms`: Total time to answer the question, in milliseconds
    * `chat_title`: The title of the deep analysis chat, if applicable
    * `user_feedback`: User feedback submitted on the chat, if applicable

    ```graphql GraphQL Query theme={null}
    query listAIHistory(
            $limit: NonNegativeInt!,
            $offset: NonNegativeInt,
            $params: AIHistoryParamsInput) {
        ai_history(limit: $limit, offset: $offset, params: $params) {
            question_id
            response_type
            question
            asked_by
            client
            agent
            conversation_id
            execution_time
            creation_time
            llm_model
            status
            sql
            error
            runtime_ms
            chat_title
            user_feedback
        }
    }
    ```

    ```json Example: Filter by feedback theme={null}
    {
      "limit": 50,
      "offset": 0,
      "params": {
        "has_feedback": true,
        "from_execution_time": "2025-01-01T00:00:00Z",
        "to_execution_time": "2025-12-31T23:59:59Z"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Publish to BI Tools

Publish a [domain](/docs/domains) directly to a BI tool, or generate the metadata artifact for it
and load it into the tool yourself.

Publishing to a server requires the matching connector to be configured in Honeydew first,
from the user settings menu under **Power BI** / **Sigma** / **Tableau** / **ThoughtSpot** →
**Settings**. See the setup instructions for
[Power BI](/docs/integration/bi-tools/powerbi-api-integration),
[Sigma](/docs/integration/bi-tools/sigma-api-integration),
[Tableau](/docs/integration/bi-tools/tableau-integration#api-access-setup) and
[ThoughtSpot](/docs/integration/bi-tools/thoughtspot-api-integration).
A connector configured this way is named `default` — pass that as `connector_name`.
Administrators can list the configured names with the queries under **Connectors** below.

Unless stated otherwise, `domain` is optional — omit it to use the full workspace model
instead of a single domain.

#### Connectors

<AccordionGroup>
  <Accordion title="List Configured BI Connectors">
    `powerbi_connectors_names`, `sigma_connectors_names`, `tableau_connectors_names` and
    `thoughtspot_connectors_names` each list the connector names configured for their tool,
    to pass as `connector_name`.
    A connector configured through the Honeydew UI is named `default`.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin

    **Return Value:**
    Each query returns the names of the connectors configured for its tool.

    ```graphql GraphQL Query theme={null}
    query biConnectorsNames {
        powerbi_connectors_names
        sigma_connectors_names
        tableau_connectors_names
        thoughtspot_connectors_names
    }
    ```
  </Accordion>
</AccordionGroup>

#### Lightdash

<AccordionGroup>
  <Accordion title="Get Lightdash Model For Domain">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `domain`: The domain to retrieve the Lightdash model for

    **Return Value:**
    Returns the Lightdash model for the domain as a string.
    For more information, see the [Lightdash Metadata Sync](/docs/integration/bi-tools/lightdash#metadata-sync) documentation.

    ```graphql GraphQL Query theme={null}
    query lightdashDbtModel($domain: String) {
        lightdash_dbt_model(domain: $domain)
    }
    ```
  </Accordion>
</AccordionGroup>

#### Looker

<AccordionGroup>
  <Accordion title="Get Looker LookML For Domain">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `domain`: The domain to retrieve the Looker LookML for

    **Return Value:**
    Returns the Looker LookML for the domain as a string.
    For more information, see the [Looker Metadata Sync](/docs/integration/bi-tools/looker#metadata-sync) documentation.

    ```graphql GraphQL Query theme={null}
    query lookmlModel($domain: String) {
        lookml_model(domain: $domain)
    }
    ```
  </Accordion>
</AccordionGroup>

#### Power BI

<AccordionGroup>
  <Accordion title="Publish Domain to Power BI Service">
    Publishes a domain as a semantic model to a Power BI workspace, then refreshes the model.
    Creates the model if it does not exist yet, and updates it otherwise.
    For more information, see the
    [Power BI API integration](/docs/integration/bi-tools/powerbi-api-integration) documentation.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `connector_name`: The name of the Power BI connector to use
    * `model_name`: The name of the semantic model to create or update
    * `group_id`: The ID of the Power BI workspace to publish into.
      Use the `powerbi_workspaces` query below to list the available workspaces.
    * `domain`: The domain to publish

    **Return Value:**

    * `semantic_model_url`: Link to the semantic model in the Power BI Service
    * `refresh_error`: Error message if the refresh that follows the publish failed.
      The model is published either way.

    ```graphql GraphQL Mutation theme={null}
    mutation syncPowerBIDatasource(
            $connector_name: String!,
            $model_name: String!,
            $group_id: String!,
            $domain: String) {
        sync_powerbi_datasource(
                connector_name: $connector_name,
                model_name: $model_name,
                group_id: $group_id,
                domain: $domain) {
            semantic_model_url
            refresh_error
        }
    }
    ```
  </Accordion>

  <Accordion title="Download Power BI Template (PBIT) For Domain">
    Generates a Power BI template for a domain, to open in
    [Power BI Desktop](/docs/integration/bi-tools/powerbi-integration#power-bi-desktop).

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `domain`: The domain to generate the template for

    **Return Value:**
    Returns the contents of the `.pbit` file, Base64-encoded.
    Decode it and save it with a `.pbit` extension.

    ```graphql GraphQL Mutation theme={null}
    mutation createPowerBIDatasourceAsPbit($domain: String) {
        create_powerbi_datasource_as_pbit(domain: $domain)
    }
    ```
  </Accordion>

  <Accordion title="Download Power BI Report (PBIX) For Domain">
    Generates a Power BI report for a domain, to open in
    [Power BI Desktop](/docs/integration/bi-tools/powerbi-integration#power-bi-desktop).

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `domain`: The domain to generate the report for

    **Return Value:**
    Returns the contents of the `.pbix` file, Base64-encoded.
    Decode it and save it with a `.pbix` extension.

    ```graphql GraphQL Mutation theme={null}
    mutation createPowerBIDatasourceAsPbix($domain: String) {
        create_powerbi_datasource_as_pbix(domain: $domain)
    }
    ```
  </Accordion>

  <Accordion title="List Power BI Workspaces">
    Lists the Power BI workspaces the connector can reach, to pick a `group_id` to publish into.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the Power BI connector to use

    **Return Value:**

    * `id`: The ID of the workspace, to pass as `group_id`
    * `name`: The name of the workspace
    * `honeydew_datasets`: The semantic models in the workspace that are connected to Honeydew
      as a source, each with its `id`, `name` and `group_id`

    ```graphql GraphQL Query theme={null}
    query powerBIWorkspaces($connector_name: String!) {
        powerbi_workspaces(connector_name: $connector_name) {
            id
            name
            honeydew_datasets {
                id
                name
                group_id
            }
        }
    }
    ```
  </Accordion>
</AccordionGroup>

#### Sigma

<AccordionGroup>
  <Accordion title="Publish Domain to Sigma">
    Publishes a domain as a Sigma data model. For more information, see the
    [Sigma](/docs/integration/bi-tools/sigma#publishing-a-domain-as-a-data-model) documentation.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `connector_name`: The name of the Sigma connector to use
    * `connection_id`: The ID of the Sigma connection to the data warehouse.
      Use the `sigma_connections` query below to list the available connections.
    * `folder_id`: The ID of the Sigma folder to publish into.
      Use the `sigma_folders` query below to list the available folders.
    * `domain`: The domain to publish
    * `model_name`: The name of the data model. Defaults to the display name of the domain.
    * `existing_data_model_id`: The ID of a data model to update.
      Omit it to create a new data model.
    * `tags`: [Version tags](/docs/integration/bi-tools/sigma#version-tags) to apply to the
      published version

    **Return Value:**

    * `data_model_url`: Link to the data model in Sigma
    * `data_model_id`: The ID of the data model, to pass as `existing_data_model_id` on the
      next publish
    * `tag_error`: Error message if applying the version tags failed.
      The data model is published either way.

    ```graphql GraphQL Mutation theme={null}
    mutation syncSigmaDatasource(
            $connector_name: String!,
            $connection_id: String!,
            $folder_id: String!,
            $domain: String,
            $model_name: String,
            $existing_data_model_id: String,
            $tags: [String!]) {
        sync_sigma_datasource(
                connector_name: $connector_name,
                connection_id: $connection_id,
                folder_id: $folder_id,
                domain: $domain,
                model_name: $model_name,
                existing_data_model_id: $existing_data_model_id,
                tags: $tags) {
            data_model_url
            data_model_id
            tag_error
        }
    }
    ```
  </Accordion>

  <Accordion title="List Sigma Connections">
    Lists the Sigma connections to the data warehouse, to pick a `connection_id` to publish against.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the Sigma connector to use

    **Return Value:**

    * `connection_id`: The ID of the connection
    * `name`: The name of the connection

    ```graphql GraphQL Query theme={null}
    query sigmaConnections($connector_name: String!) {
        sigma_connections(connector_name: $connector_name) {
            connection_id
            name
        }
    }
    ```
  </Accordion>

  <Accordion title="List Sigma Folders">
    Lists Sigma folders, to pick a `folder_id` to publish into.
    With neither optional parameter, returns the top-level folders of the default Sigma workspace.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the Sigma connector to use
    * `workspace_id`: List the top-level folders of this Sigma workspace.
      Use the `sigma_workspaces` query below to list the available workspaces.
    * `parent_id`: List the sub-folders of this folder

    `parent_id` and `workspace_id` are mutually exclusive.

    **Return Value:**

    * `id`: The ID of the folder, to pass as `folder_id`
    * `name`: The name of the folder
    * `path`: The full path of the folder

    ```graphql GraphQL Query theme={null}
    query sigmaFolders(
            $connector_name: String!,
            $parent_id: String,
            $workspace_id: String) {
        sigma_folders(
                connector_name: $connector_name,
                parent_id: $parent_id,
                workspace_id: $workspace_id) {
            id
            name
            path
        }
    }
    ```
  </Accordion>

  <Accordion title="List Sigma Workspaces">
    Lists the Sigma workspaces, to pick a `workspace_id` when listing folders.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the Sigma connector to use

    **Return Value:**

    * `workspace_id`: The ID of the workspace, to pass as `workspace_id` to `sigma_folders`
    * `name`: The name of the workspace

    ```graphql GraphQL Query theme={null}
    query sigmaWorkspaces($connector_name: String!) {
        sigma_workspaces(connector_name: $connector_name) {
            workspace_id
            name
        }
    }
    ```
  </Accordion>
</AccordionGroup>

#### Tableau

<AccordionGroup>
  <Accordion title="Publish Domain to Tableau Server/Cloud">
    Publishes a domain as a data source on Tableau Cloud or Tableau Server.
    For more information, see the
    [Tableau](/docs/integration/bi-tools/tableau-integration#setting-up-a-new-data-source) documentation.

    Pass either `existing_datasource_id` to update an existing data source, or both
    `datasource_name` and `project_id` to create a new one. Any other combination is an error.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `connector_name`: The name of the Tableau Server connector to use
    * `datasource_name`: The name of the data source to create
    * `project_id`: The ID of the Tableau project to create the data source in.
      Use the `tableau_projects` query below to list the available projects.
    * `existing_datasource_id`: The ID of the data source to update.
      Use the `tableau_honeydew_datasources` query below to list the Honeydew data sources.
    * `domain`: The domain to publish

    **Return Value:**

    * `datasource_url`: Link to the data source in Tableau

    ```graphql GraphQL Mutation theme={null}
    mutation syncTableauDatasource(
            $connector_name: String!,
            $datasource_name: String,
            $project_id: String,
            $existing_datasource_id: String,
            $domain: String) {
        sync_tableau_datasource(
                connector_name: $connector_name,
                datasource_name: $datasource_name,
                project_id: $project_id,
                existing_datasource_id: $existing_datasource_id,
                domain: $domain) {
            datasource_url
        }
    }
    ```
  </Accordion>

  <Accordion title="Download Tableau Data Source (TDS) For Domain">
    Generates a Tableau data source for a domain, to use as a
    [local data source](/docs/integration/bi-tools/tableau-integration#local-data-source)
    in Tableau Desktop.

    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `domain`: The domain to generate the data source for
    * `connector_class`: The Honeydew connector the data source connects through —
      `honeydew_jdbc` (the default) or `trino_jdbc`

    **Return Value:**
    Returns the contents of the `.tds` file, Base64-encoded.
    Decode it and save it with a `.tds` extension.

    ```graphql GraphQL Mutation theme={null}
    mutation createTableauDatasource(
            $domain: String,
            $connector_class: TableauConnectorClass) {
        create_tableau_datasource(domain: $domain, connector_class: $connector_class)
    }
    ```
  </Accordion>

  <Accordion title="List Tableau Projects">
    Lists the Tableau projects the connector can reach, to pick a `project_id` to publish into.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the Tableau Server connector to use

    **Return Value:**

    * `id`: The ID of the project, to pass as `project_id`
    * `name`: The name of the project
    * `parent_id`: The ID of the parent project, for a nested project

    ```graphql GraphQL Query theme={null}
    query tableauProjects($connector_name: String!) {
        tableau_projects(connector_name: $connector_name) {
            id
            name
            parent_id
        }
    }
    ```
  </Accordion>

  <Accordion title="List Honeydew Data Sources in Tableau">
    Lists the data sources that Honeydew published, to pick an `existing_datasource_id` to update.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the Tableau Server connector to use
    * `project_id`: Limit the results to a single Tableau project

    **Return Value:**

    * `id`: The ID of the data source, to pass as `existing_datasource_id`
    * `name`: The name of the data source
    * `project_id`: The ID of the project the data source is in

    ```graphql GraphQL Query theme={null}
    query tableauHoneydewDatasources($connector_name: String!, $project_id: String) {
        tableau_honeydew_datasources(
                connector_name: $connector_name,
                project_id: $project_id) {
            id
            name
            project_id
        }
    }
    ```
  </Accordion>
</AccordionGroup>

#### ThoughtSpot

<AccordionGroup>
  <Accordion title="Publish Domain to ThoughtSpot">
    Publishes a domain as a table on a ThoughtSpot server.
    For more information, see the
    [ThoughtSpot Metadata Sync](/docs/integration/bi-tools/thoughtspot#metadata-sync) documentation.

    **Workspace/Branch Headers:** Required

    **Permissions:** Editor or higher

    **Parameters:**

    * `connector_name`: The name of the ThoughtSpot connector to use
    * `connection_name`: The name of the Honeydew connection in ThoughtSpot to use.
      Use the `thoughtspot_connections` query below to list the available connections.
    * `domain`: The domain to publish. Required.
    * `table_name`: The name of the table to create or update.
      Defaults to the display name of the domain.

    **Return Value:**

    * `table_url`: Link to the table in ThoughtSpot
    * `table_guid`: The ID of the table in ThoughtSpot

    ```graphql GraphQL Mutation theme={null}
    mutation syncThoughtSpotDatasource(
            $connector_name: String!,
            $connection_name: String!,
            $domain: String!,
            $table_name: String) {
        sync_thoughtspot_datasource(
                connector_name: $connector_name,
                connection_name: $connection_name,
                domain: $domain,
                table_name: $table_name) {
            table_url
            table_guid
        }
    }
    ```
  </Accordion>

  <Accordion title="Get ThoughtSpot TML For Domain">
    **Workspace/Branch Headers:** Required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `domain`: The domain to retrieve the ThoughtSpot TML for
    * `connection_name`: The name of the Honeydew connection in ThoughtSpot to use

    **Return Value:**
    Returns the ThoughtSpot TML for the domain as a string.
    For more information, see the [ThoughtSpot Metadata Sync](/docs/integration/bi-tools/thoughtspot#metadata-sync) documentation.

    ```graphql GraphQL Query theme={null}
    query thoughtSpotTML($domain: String, $connection_name: String!) {
        thoughtspot_tml(domain: $domain, connection_name: $connection_name)
    }
    ```
  </Accordion>

  <Accordion title="List ThoughtSpot Connections">
    Lists the connections to the data warehouse in ThoughtSpot, to pick a `connection_name`.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Viewer or higher

    **Parameters:**

    * `connector_name`: The name of the ThoughtSpot connector to use

    **Return Value:**

    * `name`: The name of the connection, to pass as `connection_name`
    * `connection_id`: The ID of the connection

    ```graphql GraphQL Query theme={null}
    query thoughtSpotConnections($connector_name: String!) {
        thoughtspot_connections(connector_name: $connector_name) {
            name
            connection_id
        }
    }
    ```
  </Accordion>
</AccordionGroup>

### API Keys

Create and manage [API keys](/docs/access-control/api-keys) programmatically, instead of using
**Settings** → **API Keys** in the Honeydew UI. Both organization-level keys, used as service
accounts, and per-user keys are supported.

<Warning>
  The API secret is returned only once, in the response to the creation mutation.
  Store it securely — it cannot be retrieved again afterwards.
</Warning>

<Note>
  The `api_key` value (for example, `hdapi_a1b2c3d4e5f6g7h8`) identifies the key and is used as the
  username for authentication. Management mutations that take an `api_key` parameter expect this
  value, not the secret.
</Note>

<AccordionGroup>
  <Accordion title="Create Organization-Level API Key">
    Creates an organization-level API key, typically used as a service account.
    The key has its own role, independent of any user.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin

    **Parameters:**

    * `api_key_name`: A descriptive name for the key. Must not be empty, and is limited to
      100 characters.
    * `role`: The role assigned to the key — `Admin`, `Editor`, or `Viewer`.
      See [User Roles](/docs/access-control/user-access-control#user-roles).
    * `workspaces`: An optional list of workspace names the key can access.
      Applies only to `Editor` and `Viewer` keys, when
      [workspace-level access](/docs/governance/workspaces#workspace-level-access-control)
      is enabled for the organization. When it is enabled, omitting this parameter, or passing `null`
      or an empty list, leaves the key with no workspace access. `Admin` keys, and all keys in an
      organization without workspace-level access, reach every workspace.

    **Return Value:**

    * `api_key_name`: The name of the created key
    * `api_key`: The generated key identifier, used as the username for authentication
    * `api_secret`: The generated secret, used as the password for authentication

    ```graphql GraphQL Mutation theme={null}
    mutation createOrgApiKey(
            $api_key_name: String!,
            $role: Role!,
            $workspaces: [String!]) {
        create_org_api_key(
                api_key_name: $api_key_name,
                role: $role,
                workspaces: $workspaces) {
            api_key_name
            api_key
            api_secret
        }
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "create_org_api_key": {
          "api_key_name": "tableau-integration",
          "api_key": "hdapi_a1b2c3d4e5f6g7h8",
          "api_secret": "hds_..."
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Create Per-User API Key">
    Creates an API key associated with a specific user. The key inherits the role and workspace
    access of that user, so it cannot be granted more permissions than the user has.

    <Note>
      Contact [support@honeydew.ai](mailto:support@honeydew.ai) to enable
      [per-user API keys](/docs/access-control/api-keys#per-user-api-keys) for your organization.
    </Note>

    **Workspace/Branch Headers:** Not required

    **Permissions:** Depends on the per-user API keys mode configured for the organization:

    * **Admin-Only Mode**: Admin
    * **User Self-Service Mode**: any role, to create a key for themselves

    Creating a key for another user, by passing `assigned_user_email`, always requires Admin.

    **Parameters:**

    * `api_key_name`: A descriptive name for the key. Must not be empty, and is limited to
      100 characters.
    * `assigned_user_email`: The email of the user to assign the key to.
      Omit or pass `null` to create a key for the calling user.

    **Return Value:**

    * `api_key_name`: The name of the created key
    * `api_key`: The generated key identifier, used as the username for authentication
    * `api_secret`: The generated secret, used as the password for authentication

    ```graphql GraphQL Mutation theme={null}
    mutation createUserApiKey(
            $api_key_name: String!,
            $assigned_user_email: String) {
        create_user_api_key(
                api_key_name: $api_key_name,
                assigned_user_email: $assigned_user_email) {
            api_key_name
            api_key
            api_secret
        }
    }
    ```

    ```json Result Example theme={null}
    {
      "data": {
        "create_user_api_key": {
          "api_key_name": "my-notebook",
          "api_key": "hdapi_i9j8k7l6m5n4o3p2",
          "api_secret": "hds_..."
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="List API Keys">
    Lists the active API keys in the organization. Secrets are never returned.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin. In **User Self-Service Mode**, any role can run the query, and
    non-admins see only the keys assigned to them.

    **Return Value:**

    * `name`: The descriptive name given to the key on creation
    * `api_key`: The key identifier
    * `created_at`: When the key was created
    * `role`: The role of the key
    * `last_login`: When the key was last used to authenticate, or `null` if never used
    * `workspaces`: The workspaces the key is restricted to, or `null` if no list was set. `null` is
      not a grant of access to everything — with no stored list, access follows the key role and the
      organization workspace-level access setting.
      For per-user keys, this is inherited from the assigned user.
    * `assigned_user`: The user a per-user key belongs to, or `null` for an organization-level key:
      * `name`: The display name of the user
      * `email`: The email of the user
      * `role`: The role of the user, which the key inherits

    ```graphql GraphQL Query theme={null}
    query listApiKeys {
        api_keys {
            name
            api_key
            created_at
            role
            last_login
            workspaces
            assigned_user {
                name
                email
                role
            }
        }
    }
    ```
  </Accordion>

  <Accordion title="Delete API Key">
    Revokes an API key. Any integration using the key stops authenticating immediately.

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin. In **User Self-Service Mode**, users can delete keys assigned to them.

    **Parameters:**

    * `api_key`: The identifier of the key to delete, as returned by the `api_keys` query

    ```graphql GraphQL Mutation theme={null}
    mutation deleteApiKey($api_key: String!) {
        delete_api_key(api_key: $api_key)
    }
    ```
  </Accordion>

  <Accordion title="Update API Key Role">
    Changes the role of an organization-level API key.

    <Note>
      Not supported for per-user API keys, which follow the role of the user they are assigned to.
    </Note>

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin

    **Parameters:**

    * `api_key`: The identifier of the key to update
    * `role`: The new role — `Admin`, `Editor`, or `Viewer`

    ```graphql GraphQL Mutation theme={null}
    mutation updateApiKeyRole($api_key: String!, $role: Role!) {
        update_api_key_role(api_key: $api_key, role: $role)
    }
    ```
  </Accordion>

  <Accordion title="Update API Key Workspaces">
    Changes which workspaces an organization-level API key can access.

    <Note>
      Not supported for per-user API keys, which inherit workspace access from the user they are
      assigned to.
    </Note>

    **Workspace/Branch Headers:** Not required

    **Permissions:** Admin

    **Parameters:**

    * `api_key`: The identifier of the key to update
    * `workspaces`: The list of workspace names the key can access.
      Every name must be an existing workspace.
      When workspace-level access is enabled for the organization, passing an empty list leaves the
      key with no workspace access.

    <Note>
      There is no list value that grants access to all workspaces. A key reaches every workspace only
      when it has the `Admin` role, or when workspace-level access is not enabled for the organization.
      To give an `Editor` or `Viewer` key broader access, pass the full list of workspace names.
    </Note>

    ```graphql GraphQL Mutation theme={null}
    mutation updateApiKeyWorkspaces($api_key: String!, $workspaces: [String!]!) {
        update_api_key_workspaces(api_key: $api_key, workspaces: $workspaces)
    }
    ```
  </Accordion>
</AccordionGroup>

**Missing an API query or mutation?**

If you need a specific query or mutation that is not covered in this guide,
please reach out to [support@honeydew.ai](mailto:support@honeydew.ai)
