# Inviting new users
Source: https://docs.synq.io/admin/invite_user
Only users with admin-level permission can invite new users
1. Navigate to `Settings` → `Team`
2. Click `Add user`
3. Fill in the required fields
The invited user will receive an email with login details to Coalesce Quality.
# Monitor Usage
Source: https://docs.synq.io/admin/monitor-usage
Coalesce Quality is priced based on the number of monitors and users you have. Our admin panel gives you a real-time overview of how many monitors you’ve deployed.
### See monitor usage
* Head to [Settings](https://app.synq.io/settings/integrations) > [Usage](https://app.synq.io/settings/usage)
* The chart and table show the number of custom and automated monitors over time. See [Monitors overview](/monitors/monitors-overview) for a breakdown of what makes up custom and automated monitors.
See [setting up monitors](/monitors/setting-up-monitors) for guidelines on how to configure monitors. [We’re always up for a chat](/support/support) if you want specific advice on how to best deploy your monitors
# Single Sign-On (SSO)
Source: https://docs.synq.io/admin/sso
Connect your identity provider so your team signs in to Coalesce Quality with your organization’s credentials
Single Sign-On (SSO) lets your team sign in to Coalesce Quality using your organization’s identity provider (IdP) — such as Microsoft Entra ID (Azure AD), Okta, Google Workspace, or any provider that supports SAML 2.0 or OpenID Connect (OIDC). Authentication is handled by your IdP, so users never need a separate Coalesce Quality password, and you keep central control over access, password policy, and offboarding.
SSO is an enterprise feature. Connections are set up together with our team — reach out to your Coalesce contact or email [support@coalesce.io](mailto:support@coalesce.io) to start.
## How SSO works at Coalesce
Coalesce Quality uses [Auth0](https://auth0.com/docs/authenticate/enterprise-connections) as its authentication layer. Setting up SSO is a short, guided exchange between you and our team:
Register Coalesce Quality as an application (sometimes called an “enterprise app”, “integration”, or “relying party”) in your IdP.
We give you the Coalesce Quality URL to add to your app, and you send us your app’s connection details in return.
Our team creates the enterprise connection on the Coalesce Quality side and links it to your workspace.
You verify a test login, then we enable SSO for your workspace.
## Before you begin
* **Administrator access to your identity provider** — you need to be able to create applications and assign users in your IdP.
* **An enterprise plan** with SSO enabled. Contact your Coalesce account team if you’re unsure whether it’s enabled.
* **Your region** — Coalesce Quality is deployed in three regions. Note which one your workspace uses: when you register an OAuth/OIDC app you’ll enter its **callback URL** as the redirect URI. (For SAML connections, Coalesce gives you a connection-specific ACS URL based on this.)
| Region | App URL | Callback URL |
| - | - | - |
| **EU** (default) | `https://app.synq.io` | `https://auth.eu.coalescesoftware.io/login/callback` |
| **US** | `https://app.us.synq.io` | `https://auth.us.coalescesoftware.io/login/callback` |
| **AU** | `https://app.au.synq.io` | `https://auth.apac.coalescesoftware.io/login/callback` |
## Step 1: Connect your identity provider
Select your identity provider below and follow its instructions end to end. Each one lists what you’ll need, the steps to perform in your IdP, and what to send back to us. The generic **SAML 2.0** and **OIDC** tabs cover any provider not listed — including Ping Identity, ADFS, OneLogin, and JumpCloud (see [Other identity providers](#other-identity-providers)).
**What you need:** the [**Redirect URI**](#before-you-begin) for your region.
**Steps:**
1. In the [Microsoft Entra admin center](https://entra.microsoft.com), go to **Identity → Applications → App registrations → New registration**.
2. Give it a name, choose the supported account types for your organization, and add the **Redirect URI** (platform **Web**) for your region.
3. Under **Certificates & secrets**, create a new **client secret** and copy its value immediately — it can’t be viewed again later.
4. Under **API permissions**, make sure **User.Read** is granted and grant admin consent. Add **Directory.Read.All** only if you need group or extended attributes.
5. Assign the users or groups who should have access.
**What you send:**
* **Application (client) ID**
* **Client secret**
* Your **Microsoft Entra (Azure AD) domain**
**What you need:** the [**Sign-in redirect URI**](#before-you-begin) for your region.
**Steps:**
1. In the Okta Admin Console, go to **Applications → Applications → Create App Integration**.
2. Select **OIDC – OpenID Connect** and **Web Application**, then click **Next**.
3. Add the **Sign-in redirect URI** for your region.
4. Under the **Assignments** tab, assign the users or groups who should have access.
5. Save the app, then copy its **Client ID** and **Client secret**.
**What you send:**
* **Client ID**
* **Client secret**
* Your **Okta domain** (`https://.okta.com`)
**What you need:** the [**Authorized redirect URI**](#before-you-begin) for your region.
**Steps:**
1. In the [Google Cloud Console](https://console.cloud.google.com), configure the **OAuth consent screen** for your organization (User type **Internal**).
2. Go to **APIs & Services → Credentials → Create credentials → OAuth client ID** and choose **Web application**.
3. Add the **Authorized redirect URI** for your region.
4. Create the client, then copy its **Client ID** and **Client secret**.
**What you send:**
* **Client ID**
* **Client secret**
* Your **Google Workspace domain**
For any IdP that supports SAML 2.0.
**What you need:** the **ACS URL** and **SP Entity ID / Audience** for your connection — Coalesce provides both.
**Steps:**
1. Create a new SAML application / relying party for Coalesce Quality.
2. Set the **Assertion Consumer Service (ACS) URL** and **SP Entity ID / Audience** to the values Coalesce provides. The ACS URL is your region’s callback URL with a `?connection=` parameter, and the Audience is a `urn:auth0:...` identifier.
3. Configure the assertion to send the user’s email (as the Name ID or an attribute) and name (see [Map user attributes](#step-2-map-user-attributes)).
4. Assign the relevant users or groups.
**What you send:**
* The IdP **SAML metadata XML** (or metadata URL) — this includes the IdP Entity ID / Issuer, the SSO endpoint URL, and the X.509 signing certificate
* If you can’t share metadata, send those three values individually
For any IdP that supports OpenID Connect.
**What you need:** the [**Redirect URL**](#before-you-begin).
**Steps:**
1. Register a new OIDC / OAuth 2.0 application for Coalesce Quality.
2. Set the **Redirect URL** to the callback URL for your region.
3. Enable the `openid`, `profile`, and `email` scopes (this sends the email and name; see [Map user attributes](#step-2-map-user-attributes)).
4. Assign the relevant users or groups.
**What you send:**
* **Client ID**
* **Client secret**
* **Issuer URL** (the OIDC discovery base, e.g. `https://your-idp.example.com`)
Send the **client secret** over a secure channel — a secrets manager, or a 1Password shared item or one-time link — never plain email or chat. The client ID, domain, and SAML metadata aren’t sensitive and can be shared normally.
## Step 2: Map user attributes
Coalesce Quality identifies users by their email address and uses their name for display. Make sure your IdP sends these claims:
| Attribute | Required | Notes |
| - | - | - |
| **Email** | Yes | Used as the unique user identifier. For SAML, this is typically the Name ID or an `email` attribute. |
| **Name** | Recommended | Used for display. Send a full name, or first name and last name. |
For OIDC, requesting the `openid`, `profile`, and `email` scopes covers these claims.
## Step 3: Test and enforce SSO
1. Once we’ve configured the connection, we share a test login link (or you sign in from the configured app).
2. Confirm you’re redirected to your IdP, authenticate, and land in your Coalesce Quality workspace.
3. When the test succeeds, we enable SSO for your workspace.
Test with a real account from your IdP before rolling SSO out to your whole team. If attribute mapping is wrong, sign-in will fail or create users with missing details.
## User provisioning and roles
New users are provisioned automatically the first time they sign in through SSO (just-in-time provisioning). A newly provisioned user is assigned the **Business User** role, which has the most limited permissions — see [User roles](/admin/user_roles) for what each role can do.
We recommend [inviting users](/admin/invite_user) through the app UI and assigning their role **before** they sign in for the first time. Otherwise they land with the limited permissions of a Business User until an admin updates their role on the [Team page](https://app.synq.io/settings/team).
## Troubleshooting
Usually the URL you entered in your IdP doesn’t exactly match the one Coalesce provided. Re-check it for your region and that there are no trailing spaces.
For OAuth/OIDC connections (Entra ID, Okta, Google Workspace), the **client secret** has likely expired — Microsoft Entra secrets expire by default. Generate a new client secret in your IdP and send the new value to Coalesce.
The X.509 signing certificate Coalesce has doesn’t match the one your IdP is using — often after a certificate rotation. Send us your updated metadata so we can refresh the connection.
Check your attribute / claim mapping in [Map user attributes](#step-2-map-user-attributes). The email claim must be present and must be the address you expect to identify the user by.
Make sure the URLs and identifier you entered match your workspace’s region (`app.synq.io` for EU, `app.us.synq.io` for US, `app.au.synq.io` for AU). Values from another region won’t work.
## Other identity providers
Coalesce Quality works with any identity provider that supports SAML 2.0 or OpenID Connect, even if it isn’t listed in [Step 1](#step-1-connect-your-identity-provider):
* **Ping Identity (PingOne / PingFederate)** and **ADFS** — both support SAML 2.0 and OIDC. Create the application in your provider and follow the generic **SAML 2.0 (other)** or **OIDC (other)** instructions in [Step 1](#step-1-connect-your-identity-provider).
* **On-premises Active Directory / LDAP** — supported, but requires additional setup beyond the standard cloud flow. Contact [support@coalesce.io](mailto:support@coalesce.io) to connect an on-premises directory.
If you’re not sure whether your provider is supported, reach out to [support@coalesce.io](mailto:support@coalesce.io).
# User roles
Source: https://docs.synq.io/admin/user_roles
| Role | What this role is for | Example users | Key permissions |
| - | - | - | - |
| **Admin** | People responsible for configuring and operating Coalesce Quality at the workspace level | Central data platform team, Senior data engineers | • Invite and manage users • Create and manage integrations • Provision API clients and tokens • View usage statistics • Configure workspace-wide settings • Can perform all actions of all other roles |
| **Developer** | Users who build and maintain data quality workflows and data products | Data engineers, Analytics engineers, Advanced analysts | • Deploy monitors and tests • Configure owners, alerts, and notifications • Create and manage data products • Can perform all Analyst actions |
| **Analyst** | Users who need to investigate issues, view lineage, and manage incidents | Data analysts, Product analysts, BI analysts, Data governance | • Manage issues and incidents • Provide feedback to monitors • Mute alerts temporarily • Drill into observability dashboards • View definitions (owners, alerts, integrations) |
| **Business User** | Stakeholders who need visibility into data quality but do not fix issues | PMs, Finance, Growth, RevOps, Marketing, Leadership | • View data product health, status, and issues • Access issue and incident details • View current quality snapshots |
You can contact an admin user in your workspace if you think you should have admin access.\
You can see all member roles on the [team page](https://app.synq.io/settings/team)
# Email
Source: https://docs.synq.io/alert-integrations/email
Integrating Email with Coalesce Quality
See [Link ownership with email](/ownership/creating-an-owner#link-ownership-with-email)
# Microsoft Teams
Source: https://docs.synq.io/alert-integrations/ms-teams
Current capabilities:
* Integrate with the Coalesce Quality app on Microsoft Teams.
* Set up the MS Teams integration.
* Add MS Teams channels and mentions in owners.
* Receive alerts for new issues.
# Setup
* Ask your Teams admin to install the app available [here](https://github.com/getsynq/releases/raw/main/msteams/coalesceQuality-v1.2.0.zip). There are various options for app installation. The easiest would be to directly install it on the team you wish to integrate with for alerts.
* Add an integration on the [settings page](https://app.synq.io/settings/integrations). Select `MS Teams` from the list of platforms. You will need to enter the team URL to create the integration. Once you enter the correct URL, hit `Create` to finalize the creation.
# Creating or Editing Owners
To receive notifications on a Teams channel, you will need to create an owner with the channel configured or update an existing owner with the Teams channel. The following steps apply to either scenario.
* Choose `Add Owner` or `Edit` from the owner's menu.
* Under the section `MS Teams` click `@ Add teams` to enable Teams for the owner. You can then select a channel from the configured Team and optionally add mentions for channel members.
* Once you confirm the create or edit, all alerts configured for the owner will land on the selected Teams channel! 🎉
# Troubleshooting
* If you do not see the option for `MS Teams` while creating or editing an owner, you might not have added the integration. Please setup the bot and integration before trying to edit / create owners.
* If the MS Teams option is disabled, the bot might not be correctly installed on your Team. Contact your Teams admin to set up the bot as per the guide above.
# Opsgenie
Source: https://docs.synq.io/alert-integrations/opsgenie
> **Current Capabilities:**
>
> * Send information about selected issues to Opsgenie.
> * Automatically acknowledge Opsgenie alerts when issues are being worked on.
> * Close Opsgenie alerts when issues are closed.
# Setting Up in Opsgenie
1. Go to the **Teams** section and select the team you want to integrate with Opsgenie.
2. Click on the **Integrations** tab.
3. Click on the **Add Integration** button.
4. From the list of products, select **API**.
5. Enter the integration name (e.g., `Coalesce Quality`) and select the assignee team.
6. Click the **Continue** button.
7. Copy the `API Key` and save it for later use.
8. Click the **Turn on Integration** button and ensure the integration status is set to **ON**.
# Setting up in Coalesce Quality
1. Go to the [Settings](https://app.synq.io/settings/integrations) page.
2. Click on the **Add Integration** button.
3. Select **Opsgenie** from the list of platforms.
4. Enter the `API Key` obtained from the Opsgenie setup and select the appropriate region (`EU` or `US`).
5. Choose the severity of issues and the assets for which alerts should be sent to Opsgenie.
6. Click the **Create** button to complete the integration setup.
# Checking that alerts arrive
Every alert Coalesce Quality sends to Opsgenie is recorded under **Settings > Integrations > your Opsgenie integration > Deliveries**: the request made to the Opsgenie API, the response it returned, and — when nothing was sent — the reason. Use **Send test event** to open a real test alert and see the outcome, and **Redeliver** to send an earlier event again once you have changed something.
An invalid or revoked `API Key` shows up there as a rejected request rather than as silence, which is the usual reason alerts stop arriving. See [Debugging Deliveries](/api-reference/webhook-deliveries).
Opsgenie carries issue and incident lifecycle events. A delivery skipped as *Event type unsupported by integration* or *No action for lifecycle state* is an event Opsgenie has no equivalent for, not a fault.
# PagerDuty
Source: https://docs.synq.io/alert-integrations/pagerduty
Integrating PagerDuty with Coalesce Quality
# PagerDuty + Coalesce Quality integration benefits
Connecting PagerDuty with Coalesce Quality enhances your incident management and alerting capabilities, ensuring data issues are handled swiftly. Key benefits:
* **Instant Alerts:** Get real-time notifications from PagerDuty when a data issue arises.
* **Quick Impact Assessment:** Easily evaluate the downstream business impact of data issues (e.g., impacted data products) so your team can prioritize fixes.
* **Unified workflow:** On-call responders are alerted immediately, enabling faster, more efficient resolutions that fit your existing workflows.
# How it Works
* Issues triggered by monitors or transformation layer tests (from dbt, SQLMesh, or Coalesce Transform) are sent to PagerDuty as alerts, including details about the downstream impact, an error message, and the activity log
All actions taken in either tool are automatically synced, so both systems stay up-to-date.
# Requirements
* PagerDuty integrations require an Admin base role for account authorization. If you do not have this role, please contact an Admin or Account Owner within your organization to configure the integration.
# Checking that alerts arrive
Every alert Coalesce Quality sends to PagerDuty is recorded under **Settings > Integrations > your PagerDuty integration > Deliveries**: the request made to the PagerDuty Events API, the response it returned, and — when nothing was sent — the reason. Use **Send test event** to send a test alert and see the outcome, and **Redeliver** to send an earlier event again once you have changed something.
A wrong or rotated Integration Key shows up there as a rejected request rather than as silence, which is the usual reason alerts stop arriving. Each delivery also carries the PagerDuty `dedup_key`, so an incident on the PagerDuty side can be traced back to the delivery that opened it. See [Debugging Deliveries](/api-reference/webhook-deliveries).
# Support
If you need help with this integration, please reach out in our shared Slack channel.
# Integration Walkthrough
## In PagerDuty
1. There are two ways to integrate with PagerDuty: via Global Event Routing or on a PagerDuty Service. If you are adding this integration to an existing PagerDuty service, please skip to the Integrating with a PagerDuty Service section of this guide.
### Integrating With Global Event Routing
Integrating with Global Event Routing enables you to route events to specific services based on the payload of the event from your tool. If you would like to learn more, please visit our article on Global Event Routing.
1. From the Configuration menu, select Event Rules.
2. On the Event Rules screen, click on the arrow next to Incoming Event Source to display the Integration key information. Copy your Integration Key. This is the same integration key you will use for any other tool you want to integrate with using event rules. When you have finished setting up the integration in your tool, you will return to this interface to specify how to route events from your tool to services in PagerDuty.

### Integrating With a PagerDuty Service
1. From the **Configuration** menu, select **Services**.
2. There are two ways to add an integration to a service:
* **If you are adding your integration to an existing service**: Click the **name** of the service you want to add the integration to. Then, select the **Integrations** tab and click the **New Integration** button.
* **If you are creating a new service for your integration**: Please read our documentation in section [Configuring Services and Integrations](https://support.pagerduty.com/docs/services-and-integrations#section-configuring-services-and-integrations) and follow the steps outlined in the [Create a New Service](https://support.pagerduty.com/docs/services-and-integrations#section-create-a-new-service) section, selecting **Coalesce Quality** as the **Integration Type** in step 4. Continue with the "In Coalesce Quality" section below once you have finished these steps.
3. Enter an **Integration Name** in the format `monitoring-tool-service-name` (e.g., `coalesce-quality-warehouse-one-monitoring`) and select **Coalesce Quality** from the Integration Type menu.
4. Click the **Add Integration** button to save your new integration. You will be redirected to the Integrations tab for your service.
5. An **Integration Key** will be generated on this screen. Keep this key saved in a safe place, as it will be used when you configure the integration in the next section.

## In Coalesce Quality
1. Head to the **Settings** tab
2. Click **Add integration**
3. Select **PagerDuty Manual**
4. Enter the following
* Integration name: The name of your integration (default is PagerDuty)
* Sevice region: Select EU or US depending on your PagerDuty service
* Integration key: This is the Integration Key linked to your PagerDuty service integration. You can find it by opening the Integrations tab within your PagerDuty service. It’s a 32-character code.
5. Click **Create**
6. Under **Settings** you'll see your new PagerDuty integration
# Syncing the status between PagerDuty and Coalesce Quality
When taking action in PagerDuty, the following mappings apply:
* **Acknowledged**: Sets the status to *Investigating* in Coalesce Quality
* **Resolved**: Sets the status to *Fixed* in Coalesce Quality
When taking action in Coalesce Quality, the following mappings apply:
* **Investigating**: Sets the status to *Acknowledged* in PagerDuty
* **Fixed/Expected/No Action Needed**: Resolves the issue in PagerDuty
## Configuring it in Coalesce Quality
1. Ensure that the PagerDuty integration is working by navigating to **settings**
2. To set up syncing, edit the PagerDuty integration and copy the Webhook URL. You’ll need this URL to configure the Webhook in PagerDuty. When creating a new PagerDuty integration, the Webhook URL will appear after on the integration page.
## Configuring it in PagerDuty
Follow these steps to add a webhook for Coalesce Quality:
1. Go to **Services**, select the relevant service, and open the **Integrations** tab.
2. Scroll to the bottom and click on the option to **Add Webhooks** to the service.
3. Click **New Webhook**.
4. Paste the Webhook URL you obtained earlier, and add a description (e.g., `Coalesce Quality`).
5. Ensure all event subscriptions are selected.
6. Click **Add Webhook** at the bottom of the page.
7. *(Optional)* After the Webhook Subscription is created, PagerDuty will provide a secret to verify webhook payloads. Copy this secret and configure it in the PagerDuty service integration settings to authenticate events received through the webhook.
# Slack
Source: https://docs.synq.io/alert-integrations/slack
Integrating Slack with Coalesce Quality
This guide will walk you through how to set up the Slack integration, which powers our [alerting](/alerts/alert-overview). Coalesce Quality is available as a native Slack application through the marketplace, making it easy to integrate.
To be able to finish this guide, you’ll need:
→ Permission to install Slack 3rd party applications—*Slack allows this by default, but your team might have set up the [application approval process](https://slack.com/help/articles/222386767-Manage-app-approval-for-your-workspace) if you get permissions restriction. Please ask your workspace administrator to help with approval.*
⏱️ Estimated time to finish: 5 minutes.
To ensure we adhere to the principle of the least privilege, we request only minimal permissions to send Slack messages. We require the following permissions:
* **Content and info about channels & conversations** *(View emoji reactions and their associated content in channels and conversations that the app has been added to)*
* **Perform actions in channels & conversations** *(Send messages as @synq, send messages to channels @synq isn't a member of, view messages that directly mention @synq in conversations that the app is in)*
### Integrating Slack with Coalesce Quality
To connect to your Slack workspace, follow the steps below:
1. Visit [https://app.synq.io/slack](https://app.synq.io/slack)
2. Click the “Add to Slack” button that will initiate the integration flow
3. Confirm the permission grant by clicking the green “Allow” button. Depending on your Slack setup, your Slack Workspace Admin may have to approve this step.
### Private channels
If you want to send alerts to a private channel, invite the bot to the channel first by typing `@Coalesce Quality` in the channel and clicking `Invite Them`
# Alerts overview
Source: https://docs.synq.io/alerts/alert-overview
Get notified about the issues that matter, on the channels your teams already use
Alerts are built to bring you all the relevant information to triage, prioritize, and debug issues quickly. They are highly configurable and automatically populated based on your ownership, data product, and monitor settings. You can route them to multiple channels based on rules such as data warehouse schemas, metadata tags, or specific tables.
Coalesce Quality supports alerts for multiple asset types such as **monitors** (anomaly, freshness, and volume monitors), **transformation layers** (dbt, SQLMesh, Coalesce Transform—e.g., test failures, model run failures, source freshness issues), **Airflow** task and DAG failures, and **[schema changes](/monitor-types/schema-monitor)**. Alerts can be delivered to **Slack**, **email**, **Microsoft Teams**, and **webhooks**—including **PagerDuty** and **Opsgenie** through webhook integrations. You can create an alert and send it straight to the channels you choose, or attach alerts to owners so they follow your ownership model—see [Routing alerts](/alerts/routing-alerts).
*Example anomaly monitor alert*
### Core concepts
* **Group new and ongoing issues**—receiving repetitive alerts around issues you already know about is tedious. At the same time, you may still want to be reminded of them. As a compromise, we have built an approach that helps you distinguish between new and ongoing issues. See [Configuring an alert](/alerts/setting-up-alerts#configuring-an-alert) for details.
* **Automatically embed ownership**—if issues happen on a data asset with an owner, the owner is automatically displayed. If the owner has a Slack handle attached, the person or group is automatically tagged to bring awareness to the alert.
* **Recent code changes**—alerts display the most recent code change on the data asset so you can quickly confirm or rule out that the last change could have been a cause of the error and who made it. Clicking on it takes you directly to GitHub/GitLab.
* **Downstream impacted assets**—shows the number of downstream tables and BI assets to help you assess the impact. The higher the number, the more likely it is that it’s an error with a high impact. Click on it to jump to a pre-filtered lineage view.
* **Clear error message**—alerts come with built-in error messages that are processed from a source system and, where possible, cleaned to be more human-readable. For example, in dbt and Coalesce Transform, each error message has two parts (1) a human-friendly description of the error to communicate what is wrong and (2) the actual number of rows that failed the given error to help you understand the issue's magnitude.
* **Anomaly chart**—shows the chart in case of an anomaly with an overlay of historical annotations.
* **Manage status and triage issues**—change the incident status to manage the issue resolution workflow and keep track of who’s currently working on the issue.
We have limited the number of issues included in a Slack message to prevent large Slack messages that feel like a wall of text rather than a helpful alert. We default to displaying the first 15 new and five ongoing ones. If some issues get excluded, we will show “*and X more …*” below the relevant section.
### Setting up an alert
* See [Setting up alerts](/alerts/setting-up-alerts)
### Routing alerts
* See [Routing alerts](/alerts/routing-alerts)
### Configuring an alert
* See [Configuring an alert](/alerts/setting-up-alerts#configuring-an-alert)
# Business alerts
Source: https://docs.synq.io/alerts/business-alerts
A **business alert** is a regular alert with a custom [template](/alerts/templates), written in plain language for non-technical stakeholders such as marketing, operations, and sales.
Set one up like any other alert — see [Setting up alerts](/alerts/setting-up-alerts). Scope it to a specific test or monitor to tailor the message, give it a custom subject and body, and [route](/alerts/routing-alerts) it to the channels that team already uses.
# Routing alerts
Source: https://docs.synq.io/alerts/routing-alerts
Every alert can be sent to one or more **destinations**. You can send an alert straight to specific channels, route it through an owner so it follows team ownership, or do both on the same alert. The available destinations are **Slack**, **email**, **Microsoft Teams**, and **webhooks** (including **PagerDuty** and **Opsgenie**).
## Send directly to channels
When you create an alert in **Settings → Alerts**, you pick where it goes on the **Targets** step — no owner needed. Add any mix of:
* a **Slack** channel
* a **Microsoft Teams** channel
* one or more **email** addresses
* a **webhook**, including PagerDuty and Opsgenie
This is the simplest way to route an alert: the alert knows where it goes, so you can see and change its destinations in one place. Use it for alerts that don't belong to a single team — for example a company-wide freshness alert to `#data-alerts`, or a webhook that opens a PagerDuty incident.
Not every destination is offered for every alert. Slack and Microsoft Teams are available for issue and schema-change alerts, email for every alert, and webhooks for issue alerts.
## Route through an owner
Instead of naming channels on the alert, you can send it to an **owner**. The alert then goes to all the channels that owner has set up — its Slack channel, Microsoft Teams channel, email addresses, and Coalesce Quality users. You set those up on the owner (see [Setting up alerts](/alerts/setting-up-alerts) and [Tagging owners](/alerts/tagging-owners)).
*Alert routed to #operations-data-team*
Routing through an owner is handy when you want alerts to follow team ownership: change the owner's channel once and every alert sent to that owner follows. You can also give the same assets more than one owner to alert several teams at once.
## Managing routing programmatically
* **Public API** — `synq.alerts.services.v1.AlertsService` covers alerts and
their destinations. Reading needs a token with `SCOPE_ALERTS_READ`, writing
needs `SCOPE_ALERTS_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — routing that goes through an owner is managed with the
[owner tools](/scout/mcp#owners-and-ownership): an owner's channels and the
alert configuration on each of its ownerships are set in the same call that
creates it. This needs the Governance permission.
# Setting up alerts
Source: https://docs.synq.io/alerts/setting-up-alerts
There are two ways to set up alerts in Coalesce Quality:
* **Create an alert directly** in **Settings → Alerts**. You choose what it triggers on (specific assets, monitors, tests, or a data product), how it is configured, its message [template](/alerts/templates), and where it is sent — all on the alert itself. This is the most direct option and doesn't require an owner; see [Routing alerts](/alerts/routing-alerts) for the available destinations.
* **Attach alerts to an owner**, so they follow your ownership model and route through the channels configured on that owner. This is described below.
## Owner-based alerts
Owner-based alerts are set based on your ownership definitions (see [Ownership overview](/ownership/ownership-overview)).
Ownership can be defined in three main ways, serving as the backbone for owner-based alerts.
1. **All assets**—owns all assets. Use this to, e.g., create a `data team` owner and be notified of issues on any assets to your main alerting Slack channel.
2. **Custom query**—use this to select specific assets you want to be notified about using the browser or advanced filters. This is useful for setting up alerts for specific parts of your stack such as data assets in a specific database schema
3. **Data product**—use this to be notified on issues on or upstream of a data product. This is helpful for owners of data products to be aware of issues that may impact them.
### Setting up an alert for a new owner
1. Follow the steps to create a new owner and set up alerts following the step-by-step wizard: [Creating an owner](/ownership/creating-an-owner)
### Editing alerts for an existing owner
1. Navigate to `Owners` and click on the owner you want to modify
2. Click `Edit owner` to modify the owner name and its notification channels — a **Slack** channel and handles, a **Microsoft Teams** channel and mentions, **email** addresses, and **Coalesce Quality users**. Alerts routed through this owner are sent to whichever channels you set here.
3. To add additional assets to the owner, click `Add owned assets`
4. To modify existing assets or alerts, click the three dots and select `Edit definitions` to edit the asset selection or `Edit alerts` to edit the alert configuration.
### Configuring an alert
Alerts are highly configurable and can be set up to fit your needs.
1. When creating an alert for an owner or editing an existing alert, you’re presented with the following settings.
* **Severity**—choose which issue severities trigger the alert. Select any combination of `Critical`, `Error`, and `Warning` (e.g., select `Warning` to be notified of warn-level test issues from dbt, SQLMesh, or Coalesce Transform)
* **Upstream issues**—check if you want to receive alerts about upstream issues that could be impacting your data product
* **Ongoing issues alerts**—by default you are only alerted when a *new* issue is detected. Turn this on to also be reminded about issues that are still unresolved:
* **Disabled**—only alert on new issues (default)
* **Schedule**—send one reminder per day at a time you choose (UTC)
* **Each run**—alert on every failed run. Not recommended, as it often produces too many alerts
Expand **Advanced options** for a couple of extra settings:
* **SQL test audit link**—for SQL-based tests (dbt, SQLMesh, Coalesce Transform), include a link in the alert to the test audit report so recipients can inspect the rows that failed
* **Custom email reply-to**—for email alerts, set the reply-to address and the display name recipients see
# Tagging owners
Source: https://docs.synq.io/alerts/tagging-owners
If you set up an owner and link the owner to a Slack handle, the owner will automatically be tagged in the alert, making it clear to everyone responsible for addressing the alert.
Slack handles can be both individuals (e.g., `John Doe`) or groups (e.g., `product-analytics`)
It’s particularly helpful to associate Slack handles with owners when you have a central alerting channel (e.g., `#data-alerts`) and a larger data team, as it can otherwise be difficult to know who’s responsible for looking at an alert.
*The data team is being tagged for an anomaly alert*
### Setting up a Slack handle for an owner
* Follow the steps in [Link ownership to Slack](/ownership/creating-an-owner#link-ownership-with-slack)
# Writing alert templates
Source: https://docs.synq.io/alerts/templates
Customize the content and wording of your Coalesce Quality alerts
Alert templates let you control the exact content and wording of the alerts
Coalesce Quality sends so an alert reads the way your team expects.
## Choosing a template mode
Every alert offers three template modes. Pick one at the top of the template editor:
| Mode | When to use |
| - | - |
| **Standard** | The default. Uses the built-in alert format — no customization needed. |
| **Custom plain text** | Set a fixed **title** and **body** that never change. See [Plain text templates](#plain-text-templates). |
| **Custom dynamic** | Compose the message from ordered blocks and template variables. See [Dynamic templates](#dynamic-templates). |
**Alerts for business stakeholders** can be set up as templated alerts with a custom subject and body — write plain-language content that non-technical teams (marketing, operations, sales) can act on. Scope the alert to a specific test or monitor to tailor the message per rule, and [route](/alerts/routing-alerts) it to the channels they already use.
## Plain text templates
Custom plain text lets you define a fixed **title** and **body**.
Leave a field empty to use the default for that alert. Use this mode when you want a fixed,
unchanging message, like sending static content to stakeholders.
## Dynamic templates
**Alpha feature.** Customizing alert content with Dynamic templates as
described here — are currently in Alpha. Contact the
Coalesce team to have them enabled for your workspace.
Dynamic templates use block-based template layout that is interpreted by a Jinja engine.
They allow you more control over the alert content with variables and sections.
### The block model
A template is an **ordered list of blocks**. Each block is a small,
self-contained fragment written in [Jinja](https://jinja.palletsprojects.com/),
and each block renders on its own — a block cannot reference or inherit from
another block. There is no shared base template: `{% extends %}` and
`{% include %}` are **not** supported and will fail to render.
Each alert ships with a **default template** made up of the shared catalog
blocks below. When you save a custom template it **replaces the default
entirely** — so to keep a section you must keep its block, and to remove a
section you simply omit its block.
### Layout sections
A rendered alert is laid out in two sections, and every block belongs to one of
them:
| Section | When it renders |
| - | - |
| **Summary** | Once per alert — the top-level message. |
| **Issue detail** | Repeated for each issue in the alert — the issue card or attachment. |
Catalog blocks have fixed placements per channel, so they land in the right
section automatically. A **custom block** renders in whichever section you
assign it to.
### Catalog blocks
Catalog blocks land in a fixed layout section — you don't place them yourself. The
sections below are the defaults for Ungrouped Issue alerts; Grouped Issue alerts
may repeat a block across both sections.
| Block | Section | What it shows |
| - | - | - |
| `headline` | Summary | The asset that triggered the alert, linked to the issue, plus its heads and folders. |
| `products` | Summary | Affected and downstream data products. |
| `ownership` | Summary | Owners of the asset and downstream owners, tagging them where a handle is configured. |
| `error_message` | Issue detail | The issue message. |
| `asset_metadata` | Issue detail | The most recent code change and schema change on the asset (within the last 30 days). |
| `incident_mgmt` | Issue detail | Drives the Incident Management tooling. Renders in Slack only. |
### Custom blocks
You are not limited to the catalog. Add your own named block with whatever Jinja
content you like; it renders in list order within the layout section you assign
it to.
### Variables and filters
**Variables** carry the alert's data; **filters** (tagged `filter` below) format
a value. Apply a filter with a pipe: `{{ asset.name | bold }}`. Every variable is
optional — a value that isn't available for a given alert renders as empty rather
than erroring, and a filter applied to an empty value renders as empty text.
Click **Available variables** in the editor to open a reference panel listing every
variable and filter grouped by category, so you can browse and insert them without
leaving the editor.
The following are available on **every** alert:
| Name | Description |
| - | - |
| `workspace` | Workspace identifier. |
| `alert.id` | Alert identifier. |
| `issue.url` | Link to the issue (or failing run) in Coalesce Quality. |
| `issue.message` | Issue or run message. |
| `asset.name` | Asset name (truncated). |
| `asset.path` | Fully-qualified asset path. |
| `asset.type_name` | Human-readable asset type, e.g. `Table`. |
| `asset.url` | Link to the asset. |
| `asset.folders` | List of folder names the asset lives in. |
| `asset.heads` | List of head assets, each with `name`, `path`, `type_name`, `url`. |
| `owners` | Asset owners, each with `id`, `title`, `path`, `mention`. |
| `downstream_owners` | Downstream owners, same fields as `owners`. |
| `commit` | Most recent code change (within 30 days): `short_message`, `author_name`, `mailto`, `url`, `ago`. Empty if none. |
| `schema_change` | Most recent schema change (within 30 days): `url`, `ago`. Empty if none. |
| `bold` `filter` | Bold text. |
| `italic` `filter` | Italic text. |
| `link(url)` `filter` | Render the value as a hyperlink to `url`; an empty `url` degrades to the bare label. |
| `code` `filter` | Inline / block code. Pass a language, e.g. `code("sql")`. |
| `date(layout)` `filter` | Format a timestamp. Accepts a `strftime` string (e.g. `"%Y-%m-%d"`) or a Go layout; defaults to `2006-01-02 15:04:05 UTC`. |
| `join` `filter` | Join a list into a string (standard Jinja filter). |
Filters are **channel-aware**: the same template renders native Slack markup,
HTML email, or MS Teams markup depending on where the alert is sent — you don't
(and can't) branch on the channel yourself. The `mention` field on `owners` and
`downstream_owners` is already formatted for the channel, so print it directly.
#### Ungrouped Issue alerts
Alerts that fire once per issue expose these in addition to the common set:
| Name | Description |
| - | - |
| `alert.action` | `created`, `updated`, `status_updated`, or `closed`. |
| `alert.action_verb` | Human-readable action, e.g. `created`, `status updated`. |
| `issue.id` | Issue identifier. |
| `issue.group_id` | Identifier of the issue group. |
| `issue.title` | Issue title (truncated). |
| `issue.name` | Issue name. |
| `issue.muted` | Whether the issue is muted. |
| `asset.schema` | Asset schema, derived from the path. |
| `products` | Affected data products, each with `id`, `title`, `label`, `url`. |
| `downstream_products` | Downstream affected data products, same fields as `products`. |
| `incident_mgmt.set_status` | Whether the Set Status action is available. |
#### Grouped Issue alerts
Alerts that group issues by failing entity expose these in addition to the common
set:
| Name | Description |
| - | - |
| `failure.message` | Failure message. |
| `failure.started_at` | When the failing run started (a timestamp — pair with the `date` filter). |
| `failure.status_verb` | Human-readable run status, e.g. `failed`. |
| `failure.duration` | How long the failing run took, pre-formatted (e.g. `3s`). |
### Worked examples
#### A stripped-back analyst Slack alert
To give analysts a lean alert, keep the headline, affected products, ownership,
and error message, and drop the asset metadata and incident-management sections.
Because a custom template replaces the default, you achieve this by keeping only
the blocks you want:
| Block | Section | Content |
| - | - | - |
| `headline` | Summary | `{{ asset.type_name }} {{ asset.name \| link(issue.url) \| bold }}` |
| `products` | Summary | `{% if products %}Affected Products: {% for p in products %}{{ p.label \| link(p.url) }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}` |
| `ownership` | Summary | `{% if owners %}Owned by: {% for o in owners %}{{ o.title }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}` |
| `error_message` | Issue detail | `{{ issue.message }}` |
Omitting `asset_metadata` and `incident_mgmt` removes those sections — and
because `incident_mgmt` is gone, the Slack **Set Status** button is dropped too.
#### A headline that leads with the schema
To surface the schema in the headline, override the `headline` block on a
**per-issue alert**:
```jinja theme={null}
{{ asset.schema }}/{{ asset.name }} {{ alert.action_verb }}
```
For an issue that was just created on `analytics.orders`, this renders as
`analytics/orders created`. (Use this on per-issue alerts — `asset.schema` is not
available on grouped alerts.)
#### Adding a remediation playbook link to the summary
The summary can carry more than the default blocks. Add a **custom block** and
assign it to the **Summary** section to include a "what to do next" line — for
example, a link to your team's runbook for fixing the issue:
```jinja theme={null}
Runbook: {{ "Fix failing orders models" | link("https://wiki.example.com/runbooks/orders") }}
```
Because the link is a plain Jinja string, you can point it at any URL — a
Confluence page, a Notion doc, a GitHub README. The `link` filter renders it as a
native hyperlink in Slack, email, and Microsoft Teams alike. To make the link
follow the asset, build the URL from a variable instead of hard-coding it:
```jinja theme={null}
Runbook: {{ "Remediation steps" | link("https://wiki.example.com/runbooks/" ~ asset.name) }}
```
This new block renders alongside the headline in the alert summary, so recipients
see how to respond right next to what went wrong.
### When a template breaks
Templates are validated as you edit: the **live preview** in the editor renders
your template against a sample alert and shows any parse or render error inline,
so you can fix it before saving. Because filters are channel-aware, the same
template is previewed as it will appear in each channel — switch the preview
between **Slack**, **Email**, and **Microsoft Teams**:
If a saved template ever fails to render when a real alert fires — for example a
typo introduced later, or a variable used in a way that errors — Coalesce Quality
falls back to the **default template** for that alert and sends it. **Delivery is
never blocked and no partial message is sent.**
# Analytics overview
Source: https://docs.synq.io/analytics/analytics-overview
Coalesce Quality analytics gives you a bird's-eye view of the health of your data stack so you can systematically improve what matters most
Analytics is a helpful way to get everyone in your company on the same page regarding the state of your data. For example:
* Benchmark and monitor the performance of your most important data assets
* Keep stakeholders in the loop as you’re making improvements by tracking key metrics
* Identify gaps and segment your quality data across teams, platforms, and data products
## Data quality dimensions
All metrics are broken down into automated groupings. See [How metrics are calculated](/analytics/how-metrics-are-calculated) for which checks fall into each dimension and how every metric is computed.
| Dimension | Description |
| - | - |
| Accuracy | Ensures data correctly represents real-world facts (e.g., accepted\_values test for valid statuses, custom SQL checks for calculated metrics). |
| Completeness | Confirms all necessary data is present (e.g., not\_null test for critical columns, row count checks). |
| Consistency | Ensures related data agrees across tables (e.g., relationships test between a foreign key and its parent table, reconciliations between a source and its replica). |
| Uniqueness | Ensures no duplicate entries exist (e.g., unique test on primary key columns). |
| Timeliness | Checks data freshness and update frequency (e.g., source freshness tests from dbt or Coalesce Transform, custom timestamp lag checks). |
| Validity | Confirms data adheres to formats and rules (e.g., accepted\_values test for categorical data, regex-based custom tests for formatting). |
| Uncategorized | Everything else |
## Data quality metrics
Coalesce Quality offers a wide range of metrics to track your data quality
* **% of tests without issues** — This metric helps monitor the overall health and stability of the system. A low percentage indicates that you have systemic issues, or should reevaluate if certain tests are needed.
* **Number of tests** — This reflects the breadth and scope of testing coverage, showing how thoroughly the system is tested.
* **Number of tests with issues** — Tracking tests with issues provides a direct view into potential problem areas within your stack. A high or increasing count can indicate recurring weaknesses or instability in certain areas, highlighting where teams should prioritize fixes or additional safeguards.
* **New and resolved issues** — This metric reveals the rate of new issues and resolution, indicating your team’s responsiveness to issues. An ideal balance shows that new issues are resolved quickly, while an imbalance with more new issues than resolved ones can indicate growing technical data debt.
* **# incidents** — Unlike data test errors, incidents are explicitly declared by you. Counting the number of incidents gives you an indication of how many severe issues you’ve had across key parts of your business.
## Actioning data quality insights
* **Segment your insights** — Use the `Add Filter` functionality to segment insights by the owner, data product, and platform. This helps make the insights actionable and focused.
* **Create data quality run books** – If you're in a larger team, include clear steps around addressing each data quality dimension so it's clear for everyone. For example, if the Timeliness score is low, you can recommend steps such as adding a source freshness check in your transformation layer (dbt, Coalesce Transform) or an automated freshness monitor.
# Check categories
Source: https://docs.synq.io/analytics/check-categories
Classify every check into governance and technical categories so analytics, coverage, and reporting stay consistent across your data stack
Checks arrive from many tools, each with its own naming. A dbt `not_null`
test, a dbt `not_null_proportion` test, an `elementary` test, a SQLMesh audit,
and a hand-written SQL assertion can all be checking the same thing — that a
column has no missing values — yet none of them share a name. Left as-is,
there's no way to ask *"how well is completeness covered across my stack?"*
without knowing every tool's vocabulary.
Check categorisation solves this. Coalesce Quality categorises every check
(dbt tests, SQLMesh audits, custom assertions, …) as it is ingested, mapping
each one onto a **consistent taxonomy** regardless of which tool produced it.
That shared vocabulary is what powers the analytics dashboards, coverage
reports, and filtering across the product — so a check only shows up in the
right place if it is categorised correctly.
Categorisation happens along **two independent dimensions**:
| Dimension | Answers | Example values | Primarily used by |
| - | - | - | - |
| **Governance** | *What is the purpose of this check?* | Completeness, Accuracy, Validity, Timeliness, Uniqueness | Data governance teams tracking coverage against governance requirements |
| **Technical** | *What kind of check is this, mechanically?* | nullness, uniqueness, referential integrity, freshness | Analysts and developers navigating and organising checks |
The governance dimension maps onto the
[data quality dimensions](/analytics/analytics-overview) you see in Analytics.
The technical dimension groups checks by their validation logic — for example
every `not_null` variant across dbt, SQLMesh and custom SQL collapses into a
single `nullness` group.
A check carries **one category per dimension**. The two are resolved
independently, so a single check can be `Completeness` for governance and
`nullness` for technical at the same time.
## Where categories come from
For each dimension, a check's effective category is resolved from three
sources, in descending priority:
1. **Admin override** — a per-check category set by a workspace admin from the
**Checks** tab (see [Per-check overrides](#per-check-overrides)). Overrides
always win.
2. **Producer-explicit category** — set directly by the producer, or carried on
the source asset as a `quality.check_category` / `quality.governance_category`
annotation.
3. **Computed category** — derived by the categorisation engine from the
**categorisation rules** described below. A matching **workspace** rule wins
over a matching **global** rule.
If none of these produces a value the check is **Uncategorised** for that
dimension. When a higher tier supplies the value, the UI still shows which rule
*would* have matched — so you can see what an override or explicit value is
masking.
`quality.check_category` and `quality.governance_category` are the documented
annotation names. The original `synq.check_category` and
`synq.governance_category` names remain supported permanently, so existing
configurations do not need to change.
Both namespaces also accept an underscore in place of the dot (for example,
`quality_check_category`). A producer may prefix the annotation name; matching
by suffix means a name such as `dbt.tag.quality.check_category` resolves the
same way. Use the `quality.*` names in new configurations.
## Categorisation rules
A categorisation rule says *"checks matching this predicate get this
category"*. Rules are managed under **Health → Check categorisation**, which has
a section per dimension (**Governance**, **Technical**) plus a **Checks**
section for inspecting how individual checks were categorised.
Each rule has:
* a **category** — the value assigned to checks it matches (free-form, e.g.
`completeness` or `nullness`);
* a **priority** — when several rules in the same scope match the same check,
the highest-priority rule wins;
* a **predicate** — either a [structured predicate](#structured-predicate) or a
[CEL expression](#cel-expressions), never both.
Rules created in the app are **workspace rules** — they apply only to your
workspace and take precedence over the **global**, Coalesce Quality-provided
rules whatever their priority. A global rule applies only when no workspace rule
matches the check, so overriding a global rule takes a workspace rule that
matches, not a higher priority number. Global rules are maintained by Coalesce
Quality administrators.
### Structured predicate
The structured predicate matches on the facts a check already carries. A check
matches when **every non-empty group** below contains the check's value — an
empty group matches anything.
| Group | Matches against | Example |
| - | - | - |
| **Platforms** | The platform the check comes from | `dbt`, `SQLMesh` |
| **Asset types** | The check's asset type | `dbt test`, `SQLMesh audit` |
| **Packages** | The package providing the check | `dbt-utils`, `elementary` |
| **Kinds** | The check's validation-logic kind | `not_null`, `unique`, `accepted_values` |
| **Names** | Glob patterns against the check's name | `freshness_*` |
| **Annotation predicates** | Key/value annotations on the check | `dbt.tag` equals `pii` |
For example, a rule with **Platforms** = `dbt` and **Kinds** = `not_null`,
`not_null_proportion` matches every dbt not-null-style test and nothing else.
**Names** use `*` as a wildcard for any sequence of characters (every other
character is literal); multiple patterns are OR-ed.
**Annotation predicates** match a check's annotations by key, with one of four
modes per key:
| Mode | Matches when the annotation… |
| - | - |
| **exists** | …is present, regardless of value |
| **equals** | …equals a given value |
| **is one of** | …equals any value in a list |
| **matches regex** | …matches a regular expression |
The platform and asset-type dropdowns are populated from checks actually present
in your workspace and narrow as you pick, so you only ever see real values.
### CEL expressions
When the structured predicate isn't expressive enough — string prefixes,
regular expressions, combining conditions with `||` — switch the predicate to a
**CEL expression**. The rule matches a check when the expression evaluates to
`true`.
[CEL](https://github.com/google/cel-spec) (Common Expression Language) is a
small, safe expression language. The following variables are available:
| Variable | Type | Description |
| - | - | - |
| `platform` | `string` | Platform name, e.g. `PLATFORM_DBT` |
| `asset_type` | `string` | Asset type name, e.g. `ASSET_TYPE_DBT_TEST` |
| `package` | `string` | Package providing the check, may be empty |
| `kind` | `string` | Validation-logic kind, e.g. `not_null` |
| `name` | `string` | Short name of the check |
| `description` | `string` | Human-readable description, may be empty |
| `is_platform_native` | `bool` | `true` for checks built into the platform (vs. custom or third-party) |
| `annotations` | `map` | Key/value annotations on the check |
`PLATFORM_*` and `ASSET_TYPE_*` enum names are exposed as string constants, so
you can compare against them directly.
```python theme={null}
# every dbt not-null-style test
platform == PLATFORM_DBT && kind.startsWith("not_null")
# checks the source asset tagged as PII
annotations["dbt.tag"] == "pii"
# anything matching a naming convention, on any platform
name.matches("(?i)^freshness_.*")
# custom assertions that are not platform-native
asset_type == ASSET_TYPE_CUSTOM_TEST && !is_platform_native
```
Prefer the structured predicate when it can express your rule — it's easier to
read at a glance and cheaper to evaluate. Reach for CEL only for the cases it
can't cover.
## Live impact preview
While you edit a rule, the dialog runs a **live impact preview** against the
checks in your workspace, showing how many checks the rule would match before
you save. A rule with no predicate (an empty structured predicate and no CEL
expression) is flagged as a **catch-all** — it would match every check at its
priority — and the preview is skipped until you add at least one condition.
## Per-check overrides
The **Checks** section lists every check with its resolved Technical and
Governance categories and the source that produced each one. Workspace admins
can pin a category on an individual check with **Set explicit category**, which
sets a Technical and/or Governance value that takes precedence over both the
producer-explicit value and any rule match. Clearing both dimensions removes the
override, and the check falls back to its underlying category.
You can also create a rule straight from a check — the rule dialog opens
prefilled with that check's platform, asset type, package, and kind so it
matches checks shaped like it.
## Recategorisation
Whenever you add, edit, or delete a rule, Coalesce Quality enqueues a
**recategorisation sweep** that re-evaluates existing checks against the
updated rule set. The Check categorisation page shows the sweep's progress;
new checks are categorised as they are ingested.
## Managing categorisation programmatically
The **Check categorisation** screens are one client of the categorisation API.
The same rules and per-check overrides are available over the public API and to
an AI assistant over MCP, and all three drive the same services — so a rule
authored by an assistant is the same rule the page shows, subject to the same
precedence and the same recategorisation sweep.
* **Public API** — `synq.entities.checks.v2.ChecksCategorisationRulesService`
covers the rules (list them, preview a candidate's reach, upsert, delete, and
read the recategorisation status);
`synq.entities.checks.v2.ChecksCategoriesService` covers per-check overrides,
the workspace's category vocabulary, and the attribute values a predicate can
be built from. Reading needs a token with `SCOPE_POLICIES_READ`, writing needs
`SCOPE_POLICIES_EDIT`. See the [API reference](/api-reference/api) and
[API scopes](/api-reference/scopes).
* **MCP** — the
[check categorisation tools](/scout/mcp#check-categorisation) let an AI
assistant review what is uncategorised, draft a rule, preview its impact, and
save it once you approve. Reading is covered by read-only consent; saving or
deleting a rule and setting a per-check override need the Governance
permission, and every write is confirmation-gated.
# How metrics are calculated
Source: https://docs.synq.io/analytics/how-metrics-are-calculated
How Coalesce Quality calculates the Analytics metrics, the data quality dimensions, and the data product quality score
Every number in [Analytics](/analytics/analytics-overview) and in a data product's quality score is built from the same two counts:
* **Tests**: the distinct checks that ran at least once in a period.
* **Tests with issues**: the distinct checks that had an open issue at any point in that period.
This page explains what goes into those two counts, how checks are grouped into data quality dimensions, and how each metric is derived from them.
## What counts as a test
A test is any check that produces runs in Coalesce Quality:
* dbt tests, and dbt model, source, snapshot, seed and metric runs
* SQLMesh audits and model runs
* Coalesce Transform node runs and node tests
* Coalesce Quality monitors, including segmented monitors
* SQL tests and reconciliations
A test is counted in a period if it ran **at least once** in that period, whatever the result. A test that ran 50 times in a week counts as one test for that week.
## When a test has an issue
A test has an issue in a period when one of its issues was open at any point during that period. An issue is open from the run that raised it until the run that resolved it, so it is counted in every period it spans:
* an issue that started in the period
* an issue that was resolved in the period
* an issue that started earlier and is still open
Runs with a **warning**, **error** or **critical** status raise issues. A warning counts as an issue, so a test whose severity is set to warn still lowers your scores while it is failing.
Issues whose status is set to **Expected** or **No action needed** are not counted.
A test is counted once per period no matter how many issues it had.
## Data quality dimensions
Each test is assigned to one dimension based on its type. Tests whose type is not listed fall into **Uncategorized**.
| Dimension | Tests included |
| - | - |
| Accuracy | `accepted_values`, `accepted_range`, `dbt_expectations` column-value and column-pair range tests, SQLMesh `accepted_values`, `accepted_range`, `at_least_one` and `forall` audits, SQL tests of type `accuracy` |
| Completeness | `not_null`, `not_empty_string`, volume and custom volume monitors, field stats monitors, SQLMesh `not_null`, `not_null_proportion`, `not_empty_string` and string-length audits, Coalesce `hasNull`, SQL tests of type `completeness` |
| Consistency | `relationships`, `dbt_utils.equality`, `expect_column_pair_values_to_be_equal`, reconciliations, SQL tests of type `consistency` |
| Uniqueness | `unique`, `unique_combination_of_columns`, `expect_compound_columns_to_be_unique`, SQLMesh `unique_combination_of_columns` and `unique_values` audits, Coalesce `isDistinct`, SQL tests of type `uniqueness` |
| Timeliness | Freshness, custom freshness and table stats monitors, `dbt_utils.recency`, `expect_row_values_to_have_recent_data`, SQL tests of type `timeliness`, **and model, source, snapshot, seed and node runs** from dbt, SQLMesh and Coalesce Transform |
| Validity | `dbt_utils.expression_is_true`, custom numeric monitors, segmented monitors, SQLMesh regex-pattern and `not_accepted_values` audits, SQL tests of type `validity` |
| Uncategorized | Every other test, including custom dbt tests and any test type not listed above |
Model, source and node runs count under **Timeliness**. A dbt model that fails to build counts as a Timeliness test with an issue.
## Analytics metrics
Analytics splits the selected date range into weekly or monthly periods. Each column in a table is one period, and each row is one dimension. Every metric is calculated per period and per dimension. Values from different periods are never added together.
| Metric | How it is calculated |
| - | - |
| **Number of tests** | Tests that ran at least once in the period. |
| **Number of tests with issues** | Tests that had an open issue at any point in the period. |
| **% of tests without issues** | (Number of tests − Number of tests with issues) ÷ Number of tests × 100, to one decimal place. |
| **New and resolved issues** | Shown as `+new / resolved`. *New* is the number of tests whose issue started in the period. *Resolved* is the number of tests whose issue was resolved in the period. |
| **MTTR (hr)** | Total time from the start to the resolution of every issue resolved in the period, divided by the number of issues resolved, in hours to one decimal place. Shows **—** when no issue was resolved in the period. |
| **# Incidents** | Tests that had an open issue linked to an [incident](/incidents/incident-overview) at any point in the period. This counts affected tests, not incidents, so one incident covering three failing tests counts as 3. |
A period in which no test in a dimension ran shows **—** for that dimension.
The **Total** row adds up the counts of every dimension first, then calculates the percentage from those sums. It is not an average of the dimension percentages, so a dimension with many tests moves the total more than a dimension with few.
### Choosing what is counted
By default Analytics counts every test in the workspace. Filters narrow it to tests on matching assets. A test is included if **any** asset it checks matches the filter, so a `relationships` test between two tables counts when either table matches.
The **Product** filter includes the data product's own assets. Turn on **Include upstream assets** to also count every test on assets upstream of the product.
## Data product quality score
The data product **Overview** shows a quality score and test coverage, calculated from the same counts as Analytics over the **last 4 weeks**, including the current week.
The score covers tests on the data product's assets **and on every asset upstream of them**, so a failing test on a source table lowers the score of every product built on it.
**Quality score**
```text theme={null}
average tests per week − average tests with issues per week
─────────────────────────────────────────────────────────── × 100
average tests per week
```
The averages are taken over the 4 weeks before dividing, and the result is rounded to a whole percent. The score is **—** when no test ran in those 4 weeks.
The score is shown in:
* **Green** at 100%
* **Amber** from 90% to 99%
* **Red** below 90%
**Test coverage** is the average number of tests that ran per week over the 4 weeks, rounded to a whole number. It is a count of tests, not a percentage of assets.
**Change indicators** compare the most recent week with the week before it. The score change uses each week's own percentage. The coverage change is the difference in the number of tests that ran.
**Breakdown by dimension.** When tests from more than one dimension ran, click the score to see the same calculation for each dimension that had tests in the last 4 weeks.
# API
Source: https://docs.synq.io/api-reference/api
## Services
### IssuesService
(synq.issues.issues.v1.IssuesService)
IssuesService is a service for managing Issues.
#### BatchGetIssues
BatchGetIssues ([BatchGetIssuesRequest](#batchgetissuesrequest)) [BatchGetIssuesResponse](#batchgetissuesresponse)
#### ListIssues
ListIssues ([ListIssuesRequest](#listissuesrequest)) [ListIssuesResponse](#listissuesresponse)
#### MarkInvestigating
MarkInvestigating ([MarkInvestigatingRequest](#markinvestigatingrequest)) [MarkInvestigatingResponse](#markinvestigatingresponse)
Mark issue as being investigated.
#### MarkFixed
MarkFixed ([MarkFixedRequest](#markfixedrequest)) [MarkFixedResponse](#markfixedresponse)
Mark issue as fixed.
#### MarkExpected
MarkExpected ([MarkExpectedRequest](#markexpectedrequest)) [MarkExpectedResponse](#markexpectedresponse)
Mark issue as expected.
#### MarkNoActionNeeded
MarkNoActionNeeded ([MarkNoActionNeededRequest](#marknoactionneededrequest)) [MarkNoActionNeededResponse](#marknoactionneededresponse)
Mark issue as no action needed.
#### PostComment
PostComment ([PostCommentRequest](#postcommentrequest)) [PostCommentResponse](#postcommentresponse)
Post a comment on an issue.
### IssuesService
(synq.issues.v2.IssuesService)
IssuesService is a service for managing Issues.
#### BatchGetIssues
BatchGetIssues ([BatchGetIssuesRequest](#batchgetissuesrequest)) [BatchGetIssuesResponse](#batchgetissuesresponse)
#### ListIssues
ListIssues ([ListIssuesRequest](#listissuesrequest)) [ListIssuesResponse](#listissuesresponse)
#### MarkInvestigating
MarkInvestigating ([MarkInvestigatingRequest](#markinvestigatingrequest)) [MarkInvestigatingResponse](#markinvestigatingresponse)
Mark issue as being investigated.
#### MarkFixed
MarkFixed ([MarkFixedRequest](#markfixedrequest)) [MarkFixedResponse](#markfixedresponse)
Mark issue as fixed.
#### MarkExpected
MarkExpected ([MarkExpectedRequest](#markexpectedrequest)) [MarkExpectedResponse](#markexpectedresponse)
Mark issue as expected.
#### MarkNoActionNeeded
MarkNoActionNeeded ([MarkNoActionNeededRequest](#marknoactionneededrequest)) [MarkNoActionNeededResponse](#marknoactionneededresponse)
Mark issue as no action needed.
#### ClearStatus
ClearStatus ([ClearStatusRequest](#clearstatusrequest)) [ClearStatusResponse](#clearstatusresponse)
Clear the triage status of an issue, returning it to an untriaged state.
Reverses a previous MarkInvestigating / MarkFixed / MarkExpected /
MarkNoActionNeeded so the issue shows up again in open/untriaged views.
#### PostComment
PostComment ([PostCommentRequest](#postcommentrequest)) [PostCommentResponse](#postcommentresponse)
Post a comment on an issue.
#### DeleteComment
DeleteComment ([DeleteCommentRequest](#deletecommentrequest)) [DeleteCommentResponse](#deletecommentresponse)
Delete a comment on an issue. Only the actor who posted the comment may
delete it. Deleting an already-deleted comment succeeds.
### CommitsService
(synq.git.commits.v1.CommitsService)
#### GetDiffByChangeId
GetDiffByChangeId ([GetDiffByChangeIdRequest](#getdiffbychangeidrequest)) [GetDiffByChangeIdResponse](#getdiffbychangeidresponse)
#### GetDiffByCommit
GetDiffByCommit ([GetDiffByCommitRequest](#getdiffbycommitrequest)) [GetDiffByCommitResponse](#getdiffbycommitresponse)
### RecommendationsService
(synq.recommendations.v1.RecommendationsService)
RecommendationsService lists the changes Coalesce Quality recommends you
make, and records what you decided about them. See `Recommendation` for
what a recommendation is.
Recommendations are created and removed by Coalesce Quality itself: one
appears when something only you can change is found, and disappears once
it no longer holds. What you can do is decide whether a recommendation
stays in front of you:
* Dismiss hides it until something new is found about it: an object it
did not list before, or a different remediation.
* Ignore hides it for good.
* Restore undoes either.
Decisions are kept by recommendation id, so they keep applying when a
recommendation disappears and comes back.
A recommendation about an integration is also governed by that
integration's permissions: reading it needs a credential that can read
integrations, and deciding about it one that can edit them. One that cannot
does not see it listed.
Related services: `synq.integrations.v1.IntegrationsService` manages the
integrations recommendations can be about.
#### ListRecommendations
ListRecommendations ([ListRecommendationsRequest](#listrecommendationsrequest)) [ListRecommendationsResponse](#listrecommendationsresponse)
List the ids of the workspace's recommendations, optionally limited by
target and state. Fetch the recommendations themselves with
`BatchGetRecommendations`. Ids are returned in a stable order, so paging
is consistent while recommendations are added or removed.
Returns INVALID\_ARGUMENT for a malformed cursor.
#### BatchGetRecommendations
BatchGetRecommendations ([BatchGetRecommendationsRequest](#batchgetrecommendationsrequest)) [BatchGetRecommendationsResponse](#batchgetrecommendationsresponse)
Fetch recommendations by id. Ids that are unknown, belong to another
workspace, no longer exist because the condition cleared, or concern an
integration the credential cannot read are omitted from the response
rather than erroring.
#### DismissRecommendation
DismissRecommendation ([DismissRecommendationRequest](#dismissrecommendationrequest)) [DismissRecommendationResponse](#dismissrecommendationresponse)
Dismiss a recommendation until something new is found about it. Replaces
an ignore. Dismissing again records the recommendation as it reads now,
so it stays hidden until something newer than that is found.
Returns NOT\_FOUND when the recommendation does not exist in the
workspace, and PERMISSION\_DENIED when it concerns an integration the
credential cannot edit.
#### IgnoreRecommendation
IgnoreRecommendation ([IgnoreRecommendationRequest](#ignorerecommendationrequest)) [IgnoreRecommendationResponse](#ignorerecommendationresponse)
Ignore a recommendation for good, whatever is found about it later.
Replaces a dismissal. Ignoring an ignored recommendation succeeds without
change.
Returns NOT\_FOUND when the recommendation does not exist in the
workspace, and PERMISSION\_DENIED when it concerns an integration the
credential cannot edit.
#### RestoreRecommendation
RestoreRecommendation ([RestoreRecommendationRequest](#restorerecommendationrequest)) [RestoreRecommendationResponse](#restorerecommendationresponse)
Remove the dismissal or ignore standing against a recommendation, so it
is open again. Restoring a recommendation nobody decided about succeeds
without change.
Returns NOT\_FOUND when the recommendation does not exist in the
workspace, and PERMISSION\_DENIED when it concerns an integration the
credential cannot edit.
### CustomMonitorsService
(synq.monitors.custom\_monitors.v1.CustomMonitorsService)
#### BatchCreateMonitor
BatchCreateMonitor ([BatchCreateMonitorRequest](#batchcreatemonitorrequest)) [BatchCreateMonitorResponse](#batchcreatemonitorresponse)
#### BatchUpdateMonitor
BatchUpdateMonitor ([BatchUpdateMonitorRequest](#batchupdatemonitorrequest)) [BatchUpdateMonitorResponse](#batchupdatemonitorresponse)
#### BatchDeleteMonitor
BatchDeleteMonitor ([BatchDeleteMonitorRequest](#batchdeletemonitorrequest)) [BatchDeleteMonitorResponse](#batchdeletemonitorresponse)
#### BatchUpdateMonitorMode
BatchUpdateMonitorMode ([BatchUpdateMonitorModeRequest](#batchupdatemonitormoderequest)) [BatchUpdateMonitorModeResponse](#batchupdatemonitormoderesponse)
#### ConfigChangesOverview
ConfigChangesOverview ([ConfigChangesOverviewRequest](#configchangesoverviewrequest)) [ConfigChangesOverviewResponse](#configchangesoverviewresponse)
#### BatchUpdateMonitorSeverity
BatchUpdateMonitorSeverity ([BatchUpdateMonitorSeverityRequest](#batchupdatemonitorseverityrequest)) [BatchUpdateMonitorSeverityResponse](#batchupdatemonitorseverityresponse)
#### ListConfigsMonitors
ListConfigsMonitors ([ListConfigsMonitorsRequest](#listconfigsmonitorsrequest)) [ListConfigsMonitorsResponse](#listconfigsmonitorsresponse)
#### ListMonitors
ListMonitors ([ListMonitorsRequest](#listmonitorsrequest)) [ListMonitorsResponse](#listmonitorsresponse)
### MonitorInfoService
(synq.monitors.info.v1.MonitorInfoService)
#### MonitorInfo
MonitorInfo ([MonitorInfoRequest](#monitorinforequest)) [MonitorInfoResponse](#monitorinforesponse)
### DeploymentRulesService
(synq.monitors.automated\_monitors.v1.DeploymentRulesService)
#### GetDeployOverview
GetDeployOverview ([GetDeployOverviewRequest](#getdeployoverviewrequest)) [GetDeployOverviewResponse](#getdeployoverviewresponse)
#### GetDeployDeleteOverview
GetDeployDeleteOverview ([GetDeployDeleteOverviewRequest](#getdeploydeleteoverviewrequest)) [GetDeployDeleteOverviewResponse](#getdeploydeleteoverviewresponse)
#### DeleteDeploymentRule
DeleteDeploymentRule ([DeleteDeploymentRuleRequest](#deletedeploymentrulerequest)) [DeleteDeploymentRuleResponse](#deletedeploymentruleresponse)
#### ListDeploymentRules
ListDeploymentRules ([ListDeploymentRulesRequest](#listdeploymentrulesrequest)) [ListDeploymentRulesResponse](#listdeploymentrulesresponse)
#### ListDeploymentRulesForAsset
ListDeploymentRulesForAsset ([ListDeploymentRulesForAssetRequest](#listdeploymentrulesforassetrequest)) [ListDeploymentRulesForAssetResponse](#listdeploymentrulesforassetresponse)
#### BatchGetDeploymentRules
BatchGetDeploymentRules ([BatchGetDeploymentRulesRequest](#batchgetdeploymentrulesrequest)) [BatchGetDeploymentRulesResponse](#batchgetdeploymentrulesresponse)
#### UpsertDeploymentRule
UpsertDeploymentRule ([UpsertDeploymentRuleRequest](#upsertdeploymentrulerequest)) [UpsertDeploymentRuleResponse](#upsertdeploymentruleresponse)
#### BatchUpdateDeploymentRuleTitle
BatchUpdateDeploymentRuleTitle ([BatchUpdateDeploymentRuleTitleRequest](#batchupdatedeploymentruletitlerequest)) [BatchUpdateDeploymentRuleTitleResponse](#batchupdatedeploymentruletitleresponse)
#### DeployDeploymentRule
DeployDeploymentRule ([DeployDeploymentRuleRequest](#deploydeploymentrulerequest)) [DeployDeploymentRuleResponse](#deploydeploymentruleresponse)
#### BatchDeployDeploymentRules
BatchDeployDeploymentRules ([BatchDeployDeploymentRulesRequest](#batchdeploydeploymentrulesrequest)) [BatchDeployDeploymentRulesResponse](#batchdeploydeploymentrulesresponse)
#### BatchUpsertDeploymentRules
BatchUpsertDeploymentRules ([BatchUpsertDeploymentRulesRequest](#batchupsertdeploymentrulesrequest)) [BatchUpsertDeploymentRulesResponse](#batchupsertdeploymentrulesresponse)
#### BatchDeleteDeploymentRules
BatchDeleteDeploymentRules ([BatchDeleteDeploymentRulesRequest](#batchdeletedeploymentrulesrequest)) [BatchDeleteDeploymentRulesResponse](#batchdeletedeploymentrulesresponse)
### MonitorSegmentsService
(synq.monitors.segments.v1.MonitorSegmentsService)
Reads and names the segments of a segmented custom monitor.
A monitor configured with a segmentation expression splits the data it
monitors into one time series per distinct value of that expression, and each
of those is a segment. The label is what every downstream surface shows for a
segment: the name of the check it produces, the search result, the alert. A
segment with no label falls back to its raw value, so a monitor segmented on
something encoded (an id, a code, a hashed bucket) reads as that raw value
everywhere until the segment is named.
#### ListMonitorSegments
ListMonitorSegments ([ListMonitorSegmentsRequest](#listmonitorsegmentsrequest)) [ListMonitorSegmentsResponse](#listmonitorsegmentsresponse)
Lists the segments a monitor has ingested so far, with their raw value and
current label. A segment appears only once data has been observed for it,
so a monitor that has not run yet lists none.
#### BatchUpdateMonitorSegmentLabels
BatchUpdateMonitorSegmentLabels ([BatchUpdateMonitorSegmentLabelsRequest](#batchupdatemonitorsegmentlabelsrequest)) [BatchUpdateMonitorSegmentLabelsResponse](#batchupdatemonitorsegmentlabelsresponse)
Sets or clears the label of one or more segments.
Segments are addressed by the id ListMonitorSegments returns. A segment id
encodes a sanitised form of the segment value, so it cannot be constructed
from that value: read the ids first, then write against them.
### HistoryService
(synq.monitors.history.v1.HistoryService)
#### History
History ([HistoryRequest](#historyrequest)) [HistoryResponse](#historyresponse)
### MonitorPredictionsService
(synq.monitors.predictions.v1.MonitorPredictionsService)
Access to anomaly detection model predictions and raw metric timeseries.
#### GetMonitorPredictions
GetMonitorPredictions ([GetMonitorPredictionsRequest](#getmonitorpredictionsrequest)) [GetMonitorPredictionsResponse](#getmonitorpredictionsresponse)
Returns prediction timeseries for a monitor. The monitor must exist
and not be deleted. Defaults to the monitor's current metrics\_version.
#### GetEntityMetrics
GetEntityMetrics ([GetEntityMetricsRequest](#getentitymetricsrequest)) [GetEntityMetricsResponse](#getentitymetricsresponse)
Returns raw metric timeseries for an entity. Identity-aware: resolves
logical assets (e.g., dbt models) to their underlying physical tables.
#### BatchGetLatestEntityMetrics
BatchGetLatestEntityMetrics ([BatchGetLatestEntityMetricsRequest](#batchgetlatestentitymetricsrequest)) [BatchGetLatestEntityMetricsResponse](#batchgetlatestentitymetricsresponse)
Returns the most recent observed table-stats sample (row count, freshness) for each
requested entity. Identity-aware: resolves logical assets to their underlying physical
tables. Paths with no recent sample are omitted from the response rather than returned
with empty values — callers should treat a missing entry as "no data". Lookback matches
GetEntityMetrics (30 days).
### ScheduleService
(synq.schedule.v1.ScheduleService)
ScheduleService provides schedule evaluation utilities.
#### PreviewSchedules
PreviewSchedules ([PreviewSchedulesRequest](#previewschedulesrequest)) [PreviewSchedulesResponse](#previewschedulesresponse)
PreviewSchedules returns the next occurrences for one or more schedule expressions.
Never fails at the RPC level; per-schedule errors are returned in the response items.
Response items match request items by index.
### AtlanIntegrationService
(synq.extensions.atlan.integrations.v1.AtlanIntegrationService)
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Upserts an integration and validates it by checking if the provided API URL and token are valid.
#### Ping
Ping ([PingRequest](#pingrequest)) [PingResponse](#pingresponse)
Pings Atlan with the given connection credentials.
#### Get
Get ([GetRequest](#getrequest)) [GetResponse](#getresponse)
#### Remove
Remove ([RemoveRequest](#removerequest)) [RemoveResponse](#removeresponse)
#### Activate
Activate ([ActivateRequest](#activaterequest)) [ActivateResponse](#activateresponse)
Activate or deactivate the integration.
### AtlanProviderService
(synq.extensions.atlan.provider.v1.AtlanProviderService)
#### GetAtlanConnections
GetAtlanConnections ([GetAtlanConnectionsRequest](#getatlanconnectionsrequest)) [GetAtlanConnectionsResponse](#getatlanconnectionsresponse)
#### GetAtlanDataProducts
GetAtlanDataProducts ([GetAtlanDataProductsRequest](#getatlandataproductsrequest)) [GetAtlanDataProductsResponse](#getatlandataproductsresponse)
#### GetAtlanDomains
GetAtlanDomains ([GetAtlanDomainsRequest](#getatlandomainsrequest)) [GetAtlanDomainsResponse](#getatlandomainsresponse)
### AtlanWorkflowService
(synq.extensions.atlan.workflows.v1.AtlanWorkflowService)
#### SetConnectionMappings
SetConnectionMappings ([SetConnectionMappingsRequest](#setconnectionmappingsrequest)) [SetConnectionMappingsResponse](#setconnectionmappingsresponse)
#### RemoveConnectionMappings
RemoveConnectionMappings ([RemoveConnectionMappingsRequest](#removeconnectionmappingsrequest)) [RemoveConnectionMappingsResponse](#removeconnectionmappingsresponse)
#### GetConnectionMappings
GetConnectionMappings ([GetConnectionMappingsRequest](#getconnectionmappingsrequest)) [GetConnectionMappingsResponse](#getconnectionmappingsresponse)
#### GetProductMappings
GetProductMappings ([GetProductMappingsRequest](#getproductmappingsrequest)) [GetProductMappingsResponse](#getproductmappingsresponse)
#### GetDomainMappings
GetDomainMappings ([GetDomainMappingsRequest](#getdomainmappingsrequest)) [GetDomainMappingsResponse](#getdomainmappingsresponse)
#### Synchronize
Synchronize ([SynchronizeRequest](#synchronizerequest)) [SynchronizeResponse](#synchronizeresponse)
Run a synchronization job for the integration.
#### FetchRuns
FetchRuns ([FetchRunsRequest](#fetchrunsrequest)) [FetchRunsResponse](#fetchrunsresponse)
#### SetMappingToggles
SetMappingToggles ([SetMappingTogglesRequest](#setmappingtogglesrequest)) [SetMappingTogglesResponse](#setmappingtogglesresponse)
#### GetMappingToggles
GetMappingToggles ([GetMappingTogglesRequest](#getmappingtogglesrequest)) [GetMappingTogglesResponse](#getmappingtogglesresponse)
### AlertsService
(synq.alerts.services.v2.AlertsService)
AlertsService provides operations for managing alert configurations.
It supersedes synq.alerts.services.v1.AlertsService: entities are selected with
the canonical synq.queries.v1.Query + resolver\_ql trigger rather than the bespoke
EntityGroupQuery.
#### Create
Create ([CreateRequest](#createrequest)) [CreateResponse](#createresponse)
Create a new alert configuration.
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Creates the alert when no alert holds `fqn`, otherwise replaces the stored
alert with the state given here in a single write. Use it to make a stored
alert match a declared state, e.g. when deploying alerts from configuration
as code. See UpsertRequest for which fields cannot change.
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
Retrieves a list of alert configurations based on filters.
#### BatchGet
BatchGet ([BatchGetRequest](#batchgetrequest)) [BatchGetResponse](#batchgetresponse)
Get multiple alert configurations by their identifiers.
#### Rename
Rename ([RenameRequest](#renamerequest)) [RenameResponse](#renameresponse)
Rename an existing alert configuration.
The config can be identified by either ID or FQN.
#### UpdateTargets
UpdateTargets ([UpdateTargetsRequest](#updatetargetsrequest)) [UpdateTargetsResponse](#updatetargetsresponse)
Replace the delivery targets of an existing alert configuration.
The config can be identified by either ID or FQN.
#### UpdateSettings
UpdateSettings ([UpdateSettingsRequest](#updatesettingsrequest)) [UpdateSettingsResponse](#updatesettingsresponse)
Update the trigger and/or the kind-specific settings of an alert.
The config can be identified by either ID or FQN.
#### UpdateTemplate
UpdateTemplate ([UpdateTemplateRequest](#updatetemplaterequest)) [UpdateTemplateResponse](#updatetemplateresponse)
Set or clear the message template of an existing alert configuration.
The config can be identified by either ID or FQN.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete an existing alert configuration.
The config can be identified by either ID or FQN.
#### ToggleEnabled
ToggleEnabled ([ToggleEnabledRequest](#toggleenabledrequest)) [ToggleEnabledResponse](#toggleenabledresponse)
Enables or disables an existing alert configuration.
The config can be identified by either ID or FQN.
#### ToggleIncidentOwnerAlert
ToggleIncidentOwnerAlert ([ToggleIncidentOwnerAlertRequest](#toggleincidentowneralertrequest)) [ToggleIncidentOwnerAlertResponse](#toggleincidentowneralertresponse)
Turns the workspace's incident-owner alert on or off. This alert notifies
each incident's owner on its configured channels and has no other
configuration, so it is managed here rather than through Create / Delete and
does not appear in List or BatchGet. Read its current state with
GetIncidentOwnerAlert.
#### GetIncidentOwnerAlert
GetIncidentOwnerAlert ([GetIncidentOwnerAlertRequest](#getincidentowneralertrequest)) [GetIncidentOwnerAlertResponse](#getincidentowneralertresponse)
Reports whether the workspace's incident-owner alert is on. This alert has
no other configuration and does not appear in List or BatchGet; turn it on
or off with ToggleIncidentOwnerAlert.
#### SendTestAlert
SendTestAlert ([SendTestAlertRequest](#sendtestalertrequest)) [SendTestAlertResponse](#sendtestalertresponse)
Delivers a static test message to the given targets so you can confirm that
delivery reaches each channel before relying on a real alert. Nothing is
stored and the targets need not belong to a saved alert, so this can be
called while an alert is still being authored.
### AlertsService
(synq.alerts.services.v1.AlertsService)
AlertsService provides operations for managing alert configurations.
Deprecated: use synq.alerts.services.v2.AlertsService. The v2 service selects
entities with the canonical synq.queries.v1.Query + resolver\_ql trigger; this
v1 service, which uses the bespoke EntityGroupQuery selection, is scheduled for
removal. Migrate existing integrations to v2.
#### Create
Create ([CreateRequest](#createrequest)) [CreateResponse](#createresponse)
Create a new alert configuration.
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
Retrieves a list of alert configurations based on filters.
#### BatchGet
BatchGet ([BatchGetRequest](#batchgetrequest)) [BatchGetResponse](#batchgetresponse)
Get multiple alert configurations by their identifiers.
#### Update
Update ([UpdateRequest](#updaterequest)) [UpdateResponse](#updateresponse)
Update an existing alert configuration.
The config can be identified by either ID or FQN.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete an existing alert configuration.
The config can be identified by either ID or FQN.
#### ToggleEnabled
ToggleEnabled ([ToggleEnabledRequest](#toggleenabledrequest)) [ToggleEnabledResponse](#toggleenabledresponse)
Enables or disables an existing alert configuration.
The config can be identified by either ID or FQN.
### DomainsService
(synq.domains.v2.DomainsService)
DomainsService manages data domains — named areas of ownership that group the
assets, data products and sub-domains belonging to one part of the business.
Ids are opaque UUIDs supplied by the caller, which makes writes idempotent
(a retried create converges instead of duplicating). Mutable fields are
optional on Upsert: a field that is set is written, a field that is omitted is
left unchanged. Pass the `etag` you last read to guard against overwriting a
concurrent edit.
A domain may contain other domains and data products, so membership resolution
is recursive. Every write that adds such a reference — including a change of
`parent_id` — is rejected if it would close a loop, so a definition can always
be resolved in finite time.
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Create or update a domain. The id is supplied by the caller (a UUID);
repeating the same request converges to the same domain. Mutable fields are
optional: a set field is written, an omitted field is left unchanged. Pass
`etag` to guard against overwriting a concurrent edit.
#### BatchGet
BatchGet ([BatchGetRequest](#batchgetrequest)) [BatchGetResponse](#batchgetresponse)
Fetch multiple domains by id. Ids that do not exist (or, unless
`include_deleted` is set, are deleted) are omitted from the response rather
than causing an error.
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
List the domains in the workspace, paginated. Narrow to one level of the
hierarchy with `parent_id`.
#### FetchDomainWithSubDomains
FetchDomainWithSubDomains ([FetchDomainWithSubDomainsRequest](#fetchdomainwithsubdomainsrequest)) [FetchDomainWithSubDomainsResponse](#fetchdomainwithsubdomainsresponse)
Fetch a domain together with its direct sub-domains, keyed by id.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete a domain. Idempotent — deleting an already-deleted or unknown domain
succeeds. Soft-deletes by default; set `purge` to hard-delete and release the
id, which is refused while the domain still has sub-domains. Pass `etag` to
guard against deleting a concurrently-edited domain.
#### SetDefinition
SetDefinition ([SetDefinitionRequest](#setdefinitionrequest)) [SetDefinitionResponse](#setdefinitionresponse)
Replace the entire membership definition of a domain.
#### UpsertDefinitionPart
UpsertDefinitionPart ([UpsertDefinitionPartRequest](#upsertdefinitionpartrequest)) [UpsertDefinitionPartResponse](#upsertdefinitionpartresponse)
Add or replace a single definition part (matched by its part id).
#### RemoveDefinitionPart
RemoveDefinitionPart ([RemoveDefinitionPartRequest](#removedefinitionpartrequest)) [RemoveDefinitionPartResponse](#removedefinitionpartresponse)
Remove a single definition part by its part id. Idempotent.
#### ListMembers
ListMembers ([ListMembersRequest](#listmembersrequest)) [ListMembersResponse](#listmembersresponse)
List the assets that currently belong to a domain, resolved from its
definition — including everything contributed by the data products and
domains it references, and by its own sub-domains — and returned as opaque
entity ids. Paginated.
#### GetUsage
GetUsage ([GetUsageRequest](#getusagerequest)) [GetUsageResponse](#getusageresponse)
Report how many domains the workspace uses, split into top-level and sub-domains.
### DomainMembersService
(synq.domains.members.v1.DomainMembersService)
DomainMembersService can be used to manage domain memberships.
#### AddDomainMembers
AddDomainMembers ([AddDomainMembersRequest](#adddomainmembersrequest)) [AddDomainMembersResponse](#adddomainmembersresponse)
Add domain members.
#### RemoveDomainMembers
RemoveDomainMembers ([RemoveDomainMembersRequest](#removedomainmembersrequest)) [RemoveDomainMembersResponse](#removedomainmembersresponse)
Remove domain members.
#### ListDomainMembers
ListDomainMembers ([ListDomainMembersRequest](#listdomainmembersrequest)) [ListDomainMembersResponse](#listdomainmembersresponse)
List domain members.
#### ListUserDomains
ListUserDomains ([ListUserDomainsRequest](#listuserdomainsrequest)) [ListUserDomainsResponse](#listuserdomainsresponse)
List domains for the given user.
#### SetDefaultUserDomain
SetDefaultUserDomain ([SetDefaultUserDomainRequest](#setdefaultuserdomainrequest)) [SetDefaultUserDomainResponse](#setdefaultuserdomainresponse)
Set default domain for user.
### DomainsService
(synq.domains.v1.DomainsService)
DomainsService can be used to manage domains.
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Create or update a domain.
#### BatchGet
BatchGet ([BatchGetRequest](#batchgetrequest)) [BatchGetResponse](#batchgetresponse)
Get multiple domains by their identifiers.
#### FetchDomainWithSubDomains
FetchDomainWithSubDomains ([FetchDomainWithSubDomainsRequest](#fetchdomainwithsubdomainsrequest)) [FetchDomainWithSubDomainsResponse](#fetchdomainwithsubdomainsresponse)
Get a domain with its sub-domains
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
List all the domains in the current workspace.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete a domain by its identifier.
#### SetDefinition
SetDefinition ([SetDefinitionRequest](#setdefinitionrequest)) [SetDefinitionResponse](#setdefinitionresponse)
Set the definition for an existing domain.
#### UpsertDefinitionPart
UpsertDefinitionPart ([UpsertDefinitionPartRequest](#upsertdefinitionpartrequest)) [UpsertDefinitionPartResponse](#upsertdefinitionpartresponse)
Upsert definition part to the definition of an existing domain.
#### RemoveDefinitionPart
RemoveDefinitionPart ([RemoveDefinitionPartRequest](#removedefinitionpartrequest)) [RemoveDefinitionPartResponse](#removedefinitionpartresponse)
Remove definition part from the definition of an existing domain.
#### GetUsage
GetUsage ([GetUsageRequest](#getusagerequest)) [GetUsageResponse](#getusageresponse)
Report how many domains the workspace uses, split into top-level and sub-domains.
### QueriesService
(synq.queries.v1.QueriesService)
QueriesService compiles and evaluates ResolverQL — the compact text query
language used across Coalesce Quality to select entities — over the public
API.
ResolverQL is a string DSL (for example `with_type("TABLE")` or
`in_folder(...)`); a structured `synq.queries.v1.Query` is its parsed,
machine-readable form. This service is the bridge between the two and the
read-only execution surface for both:
* `TranslateResolverQl` compiles a ResolverQL expression into a structured
`Query` without evaluating it — inspect it, validate it, or reuse it as a
filter on another API (entity listing, folder browsing, ...).
* `ResolveQuery` compiles a ResolverQL expression (or takes an
already-structured `Query`), evaluates it against the catalog, and
returns the matched entity ids. This is the general author -> resolve
loop.
* `DescribeResolverQl` returns the language itself — every function, every
accepted enum value — so you author against what this deployment compiles
rather than against a table baked into your client.
All three authenticate and resolve your workspace from your credentials.
`DescribeResolverQl` additionally needs no permission of its own, so any
credential can read it; the other two read the catalog and are scoped.
#### DescribeResolverQl
DescribeResolverQl ([DescribeResolverQlRequest](#describeresolverqlrequest)) [DescribeResolverQlResponse](#describeresolverqlresponse)
Describe the ResolverQL this deployment accepts: every function with its
arguments and documentation, the category order to group them by, and the
accepted values of every enum-typed argument.
Read it before authoring a selection, and validate against it rather than
against a copy compiled into your client. The language gains functions and
accepted values as Coalesce Quality supports more, and a client working from
its own table both misses what was added and refuses a query this server
would compile.
Nothing is passed, and no permission is needed beyond being authenticated —
so a client holding a narrow credential, one that may write a deployment
rule but cannot read entities, can still find out how to write the selection
it is being asked for.
Read it as an answer for your workspace, not as a constant. Most of it is
the same for everyone, but the entity types you defined yourself are part of
the language too: today they are reachable only through
`ResolverQlEnumDomain.pattern`, which is why that field exists, and named by
`synq.entities.typedefs.v1.TypeDefsService.ListEntityTypeDefs`.
Caching it for the life of your process is fine; re-read it after you add or
rename an entity type of your own.
#### TranslateResolverQl
TranslateResolverQl ([TranslateResolverQlRequest](#translateresolverqlrequest)) [TranslateResolverQlResponse](#translateresolverqlresponse)
Compile a ResolverQL expression into a structured public `Query`. Unlike
`ResolveQuery`, it does not evaluate the query against the catalog. A
syntactically invalid or uncompilable expression fails with
INVALID\_ARGUMENT.
#### ResolveQuery
ResolveQuery ([ResolveQueryRequest](#resolvequeryrequest)) [ResolveQueryResponse](#resolvequeryresponse)
Evaluate a selection and return the matched entity ids, paginated. Pass a
ResolverQL expression via `resolver_ql` (it is compiled server-side) or an
already-structured `query`. Invalid ResolverQL fails with INVALID\_ARGUMENT.
### NLQueryService
(synq.queries.v1.NLQueryService)
NLQueryService generates structured Query protos from natural language descriptions using an LLM.
#### GenerateQuery
GenerateQuery ([GenerateQueryRequest](#generatequeryrequest)) [GenerateQueryResponse](#generatequeryresponse)
GenerateQuery generates a Query from a natural language description.
### IncidentsService
(synq.incidents.v1.IncidentsService)
IncidentsService is a service for managing Incidents.
#### CreateIncident
CreateIncident ([CreateIncidentRequest](#createincidentrequest)) [CreateIncidentResponse](#createincidentresponse)
#### RenameIncident
RenameIncident ([RenameIncidentRequest](#renameincidentrequest)) [RenameIncidentResponse](#renameincidentresponse)
#### UpdateIncidentState
UpdateIncidentState ([UpdateIncidentStateRequest](#updateincidentstaterequest)) [UpdateIncidentStateResponse](#updateincidentstateresponse)
#### CancelIncident
CancelIncident ([CancelIncidentRequest](#cancelincidentrequest)) [CancelIncidentResponse](#cancelincidentresponse)
Cancel an incident
#### CloseIncident
CloseIncident ([CloseIncidentRequest](#closeincidentrequest)) [CloseIncidentResponse](#closeincidentresponse)
Close an incident
#### MergeIncidents
MergeIncidents ([MergeIncidentsRequest](#mergeincidentsrequest)) [MergeIncidentsResponse](#mergeincidentsresponse)
#### SplitIncident
SplitIncident ([SplitIncidentRequest](#splitincidentrequest)) [SplitIncidentResponse](#splitincidentresponse)
#### AddIssuesToIncident
AddIssuesToIncident ([AddIssuesToIncidentRequest](#addissuestoincidentrequest)) [AddIssuesToIncidentResponse](#addissuestoincidentresponse)
#### RemoveIssuesFromIncident
RemoveIssuesFromIncident ([RemoveIssuesFromIncidentRequest](#removeissuesfromincidentrequest)) [RemoveIssuesFromIncidentResponse](#removeissuesfromincidentresponse)
#### ListIncidents
ListIncidents ([ListIncidentsRequest](#listincidentsrequest)) [ListIncidentsResponse](#listincidentsresponse)
#### BatchGetIncidents
BatchGetIncidents ([BatchGetIncidentsRequest](#batchgetincidentsrequest)) [BatchGetIncidentsResponse](#batchgetincidentsresponse)
#### AssignIncidentOwner
AssignIncidentOwner ([AssignIncidentOwnerRequest](#assignincidentownerrequest)) [AssignIncidentOwnerResponse](#assignincidentownerresponse)
#### PostComment
PostComment ([PostCommentRequest](#postcommentrequest)) [PostCommentResponse](#postcommentresponse)
Post a comment on an incident.
#### DeleteComment
DeleteComment ([DeleteCommentRequest](#deletecommentrequest)) [DeleteCommentResponse](#deletecommentresponse)
Delete a comment on an incident. Only the actor who posted the comment may
delete it. Deleting an already-deleted comment succeeds.
### OverlaysService
(synq.overlays.v1.OverlaysService)
OverlaysService answers which organizational overlays claim an entity.
An overlay is a grouping you lay over your catalog rather than something the
warehouse itself knows about. Two kinds exist today: a **data product**, which
groups the assets that make up something your organization delivers, and an
**owner**, which names the person or team responsible for a set of assets and
where alerts about them go. Each is defined by a membership rule, not by an
explicit list, so what belongs to one changes as your warehouse does.
"Owner" here means stewardship — who is accountable for an asset and who hears
about it when something breaks. It is not the structural containment sense the
word carries elsewhere in the API, where an asset can be said to own the checks
and monitors declared on it. Nothing this service returns describes that
relationship.
This service reads that membership backwards. The defining services answer
"what is in this data product"; this one answers "which data products and
owners claim this table" — the question you have when you are looking at one
asset and want to know who cares about it and what it feeds.
Membership follows identity. Where the same table is represented more than
once in your catalog — a transformation model and the warehouse table it
builds — an overlay whose rule matches one of them claims the others too. So
an alert routed by ownership reaches the responsible party whichever
representation raised it, and you do not have to know which one a rule was
written against.
Two consequences worth planning around. You can ask about whichever
representation you happen to hold and get the same answer, so there is no need
to resolve an identity group first. And if you do ask about several members of
one group in a single call, each comes back with its own entry carrying the
same overlays — correct, but redundant, so ask about one member per group when
you are near the request limit.
Related services:
* `synq.dataproducts.v2.DataproductsService` and `synq.owners.v1.OwnersService`
define the overlays read here, and turn an id from a response into a name,
a description and its contact channels.
* `synq.entities.resolve.v1.IdentifierResolveService` turns an identifier you
already hold — a warehouse table name, a dbt model — into the `entity_id`
these calls take.
#### BatchGetEntityOverlays
BatchGetEntityOverlays ([BatchGetEntityOverlaysRequest](#batchgetentityoverlaysrequest)) [BatchGetEntityOverlaysResponse](#batchgetentityoverlaysresponse)
For each entity, list every overlay that claims it.
The response has one entry per requested id, in the order you asked, so you
can match results positionally and never have to tell "absent" from "empty".
An entity that belongs to no overlay comes back with empty lists.
Freshness. Which overlays exist and what they are attached to is up to date
the moment you change it — create an overlay, delete one, point an ownership
at a different data product, and the next call reflects it. What each
overlay *contains* is recomputed periodically, so an asset that newly
satisfies an existing rule — a table appearing in a folder a rule watches —
shows up shortly afterwards rather than instantly.
Returns PERMISSION\_DENIED if your credential can read neither data products
nor owners. If it can read one but not the other, the call succeeds and the
list you may not read comes back empty rather than failing.
#### BatchGetOverlayMemberCounts
BatchGetOverlayMemberCounts ([BatchGetOverlayMemberCountsRequest](#batchgetoverlaymembercountsrequest)) [BatchGetOverlayMemberCountsResponse](#batchgetoverlaymembercountsresponse)
Count the members of each named overlay.
The cheap way to answer "how big is this" — use it for a summary line or a
badge. When you need the assets themselves, list them through the service
that defines the overlay.
Counts distinct members, so an owner responsible for two data products that
overlap is not charged twice for the assets they share.
Same freshness as `BatchGetEntityOverlays`: counts follow the periodic
recomputation of what each overlay contains.
Returns PERMISSION\_DENIED if your credential can read neither data products
nor owners.
#### ListEntityCoverage
ListEntityCoverage ([ListEntityCoverageRequest](#listentitycoveragerequest)) [ListEntityCoverageResponse](#listentitycoverageresponse)
List the candidate assets together with the overlays claiming each, and
narrow them to what is or is not covered.
This is the question behind a governance audit: of the assets you care
about, which belong to no data product, and which have nobody responsible
for them. You give the candidate set as a query — every asset, one
platform, one folder, one domain — and each row comes back carrying the
same owner and data-product lists `BatchGetEntityOverlays` returns, so a
single call is both the count and the worklist.
`total_count` is how many candidates match `filter` across every page, so
you can say "312 of 4,190 tables are in no data product" without paging to
the end.
Freshness. By default this reads the same periodically recomputed
membership the rest of this service reads, and tells you how old it is in
`members_computed_at`. Ask for `FRESHNESS_LIVE` when the answer has to
reflect every definition as of now — it re-resolves them, which is far
slower and far more expensive, so keep it for a one-off audit rather than a
page that refreshes.
Returns PERMISSION\_DENIED if your credential can read neither data products
nor owners, and also if `filter` names a kind it may not read: an empty list
must not be the answer to both "nothing is uncovered" and "you may not
look". A kind you may not read still comes back as an empty list on each
row, as it does elsewhere in this service.
Returns INVALID\_ARGUMENT if the selection resolves to more assets than one
call will report on — see `CoverageSelection` for the limit and for how to
choose a candidate set worth reading.
#### GetCoverageSummary
GetCoverageSummary ([GetCoverageSummaryRequest](#getcoveragesummaryrequest)) [GetCoverageSummaryResponse](#getcoveragesummaryresponse)
Count coverage over one or more named candidate sets, without listing the
assets themselves.
The aggregate behind a coverage dashboard. Each selection you send comes
back as one row of totals, keyed by the label you gave it, so a breakdown
by type, platform, folder or domain is one round trip rather than one call
per bucket.
There is no server-side grouping, on purpose. This service knows what claims
an asset, not what kind of thing the asset is, so naming the selections
yourself — `with_type("dbt_model")`, `in_folder(...)`, `in_domain(...)` —
keeps the buckets in your own vocabulary and costs the same.
Selections are counted independently and may overlap. An asset in two of
them is counted in both.
Returns PERMISSION\_DENIED unless your credential can read both data products
and owners. Every row names both, and a zero that was never looked at is
indistinguishable from a zero that was.
### IntegrationsService
(synq.integrations.v1.IntegrationsService)
IntegrationsService manages connections from Coalesce Quality to your data
systems (warehouses, databases, and transformation tools).
Concurrency: every integration carries an opaque `etag`. Read it from
`GetIntegration` / `ListIntegrations`, then pass it back on
`UpdateIntegration` / `DeleteIntegration` to ensure you modify the version you
last saw. A stale etag is rejected with ABORTED (HTTP 409). Omit
the etag for last-write-wins.
Quota: each workspace has a limit on the number of integrations. Creating
beyond the limit is rejected with RESOURCE\_EXHAUSTED.
Secrets: credential fields (passwords, tokens, keys) are write-only. They are
masked (returned empty) on every read. On update, omit a secret to keep it,
send a new value to rotate it, or send an explicit empty string to clear it
(where the field is `optional`).
#### CreateIntegration
CreateIntegration ([CreateIntegrationRequest](#createintegrationrequest)) [CreateIntegrationResponse](#createintegrationresponse)
Create a new integration. The server assigns the id and returns the created
integration including any generated outputs.
The integration type is fixed by the populated `config` variant and cannot
be changed later. Counts against the per-workspace quota.
Not safe to blindly retry: the server mints a new id on each call, so a
retried create produces a second integration. On a transport error, list /
look up before retrying.
#### GetIntegration
GetIntegration ([GetIntegrationRequest](#getintegrationrequest)) [GetIntegrationResponse](#getintegrationresponse)
Fetch a single integration by id. Secret config fields are masked.
#### ListIntegrations
ListIntegrations ([ListIntegrationsRequest](#listintegrationsrequest)) [ListIntegrationsResponse](#listintegrationsresponse)
List all integrations in the workspace (optionally filtered by enabled
state). Secret config fields are masked. The full set is returned in one
response — a workspace has only a handful of integrations.
#### BatchGetIntegrations
BatchGetIntegrations ([BatchGetIntegrationsRequest](#batchgetintegrationsrequest)) [BatchGetIntegrationsResponse](#batchgetintegrationsresponse)
Fetch multiple integrations by id in one call. Ids not found (or not in the
workspace) are omitted from the response rather than erroring.
#### UpdateIntegration
UpdateIntegration ([UpdateIntegrationRequest](#updateintegrationrequest)) [UpdateIntegrationResponse](#updateintegrationresponse)
Update an integration. The `config` is replaced wholesale — send the
complete desired config (omitted secrets are preserved; see the
service-level secret semantics). `title` is patch-style: omit it to leave
it unchanged.
The `config` variant must match the existing integration's type; changing
the type is rejected with FAILED\_PRECONDITION.
Safe to retry: addresses an existing id and converges to the same state.
With an `etag` set, a retry after success returns ABORTED
(the version moved) — treat that as "already applied".
#### DeleteIntegration
DeleteIntegration ([DeleteIntegrationRequest](#deleteintegrationrequest)) [DeleteIntegrationResponse](#deleteintegrationresponse)
Delete an integration. The integration stops being scheduled and is removed
from listings. Safe to retry (deleting an already-deleted id is a no-op).
#### EnableIntegration
EnableIntegration ([EnableIntegrationRequest](#enableintegrationrequest)) [EnableIntegrationResponse](#enableintegrationresponse)
Enable a previously disabled integration. No-op if already enabled.
#### DisableIntegration
DisableIntegration ([DisableIntegrationRequest](#disableintegrationrequest)) [DisableIntegrationResponse](#disableintegrationresponse)
Disable an integration without deleting it. The configuration is kept but
the integration is no longer scheduled or refreshed. No-op if already
disabled.
#### RefreshIntegration
RefreshIntegration ([RefreshIntegrationRequest](#refreshintegrationrequest)) [RefreshIntegrationResponse](#refreshintegrationresponse)
Trigger an immediate, ad-hoc refresh of an integration. Available only for
types whose `Capabilities.can_refresh` is true; otherwise rejected with
FAILED\_PRECONDITION.
Not idempotent: each call enqueues a new refresh.
#### GetIntegrationHealth
GetIntegrationHealth ([GetIntegrationHealthRequest](#getintegrationhealthrequest)) [GetIntegrationHealthResponse](#getintegrationhealthresponse)
Return current health plus a paginated history of recent runs for an
integration. When pagination is omitted, a bounded recent window is
returned (the most recent runs from the last 7 days).
### PlatformsService
(synq.platforms.v1.PlatformsService)
PlatformsService is a service for managing Platforms and Integrations.
#### SetPlatformAuthentication
SetPlatformAuthentication ([SetPlatformAuthenticationRequest](#setplatformauthenticationrequest)) [SetPlatformAuthenticationResponse](#setplatformauthenticationresponse)
### DeliveriesService
(synq.deliveries.v1.DeliveriesService)
DeliveriesService reports what was sent to your notification integrations —
webhooks, PagerDuty, Opsgenie — and what came back.
It answers the three questions an integration owner has. "Did this event
reach my endpoint": list the deliveries for an event id and read the outcome
of each. "Why did my endpoint reject it": read the attempts, which carry the
request as it was sent and the response as it came back. "Why did I get
nothing at all": every delivery that sent nothing records a reason, so a
silent integration can be explained rather than guessed at.
Deliveries are kept for 90 days. Credential-bearing header values are never
returned, on either side of an attempt — see `Header`.
#### ListDeliveries
ListDeliveries ([ListDeliveriesRequest](#listdeliveriesrequest)) [ListDeliveriesResponse](#listdeliveriesresponse)
List deliveries, newest event first. With no `integration_id` this is the
whole workspace's feed; with one it is that integration's.
#### BatchGetDeliveries
BatchGetDeliveries ([BatchGetDeliveriesRequest](#batchgetdeliveriesrequest)) [BatchGetDeliveriesResponse](#batchgetdeliveriesresponse)
Fetch deliveries by id. Ids that do not exist, or have passed out of the
retention window, are omitted from the response rather than causing an
error. Unlike `ListDeliveries`, this always returns the routing provenance.
#### GetDelivery
GetDelivery ([GetDeliveryRequest](#getdeliveryrequest)) [GetDeliveryResponse](#getdeliveryresponse)
Fetch a single delivery by id. `BatchGetDeliveries` does the same for many
at once and is the variant exposed over REST.
#### ListAttempts
ListAttempts ([ListAttemptsRequest](#listattemptsrequest)) [ListAttemptsResponse](#listattemptsresponse)
List every attempt made for one delivery, oldest first. A delivery is
attempted at most ten times, so the result is small and is not paginated.
#### SummariseDeliveries
SummariseDeliveries ([SummariseDeliveriesRequest](#summarisedeliveriesrequest)) [SummariseDeliveriesResponse](#summarisedeliveriesresponse)
Count deliveries over a window, bucketed by outcome, by skip reason and by
HTTP status class. Use it to watch your own delivery failure rate without
paging through the feed.
#### Redeliver
Redeliver ([RedeliverRequest](#redeliverrequest)) [RedeliverResponse](#redeliverresponse)
Send an earlier delivery again, to the same integration, with the same
event. The replay is a new delivery with its own id, linked to the original
through `redelivery_of`, and appears in `ListDeliveries` like any other.
It is signed with the integration's current secret rather than the one the
original used, which makes it the way to confirm that a rotated secret is
accepted by the receiver.
Retrying this call sends the delivery again. Rate-limited per integration;
over the limit it fails with `RESOURCE_EXHAUSTED` rather than queueing.
#### SendTestDelivery
SendTestDelivery ([SendTestDeliveryRequest](#sendtestdeliveryrequest)) [SendTestDeliveryResponse](#sendtestdeliveryresponse)
Send a test delivery to an integration, to confirm the endpoint is reachable
and that it accepts what is sent. It is always sent, whatever event types
the integration subscribes to — a test that silently matches nothing would
report the same as a broken endpoint.
Returns the id of the delivery it created. The send is asynchronous: poll
`BatchGetDeliveries` with that id until the outcome leaves `OUTCOME_PENDING`.
Retrying this call sends another test. Rate-limited per integration; over
the limit it fails with `RESOURCE_EXHAUSTED` rather than queueing.
### LlmService
(synq.agent.sre.v1.LlmService)
Service for evaluating LLM requests and producing structured output.
#### Evaluate
Evaluate ([EvaluateRequest](#evaluaterequest)) [EvaluateResponse](#evaluateresponse)
Evaluates an LLM request with a structured output schema and message history.
The main prompt should be constant as it will be cached for efficiency.
### SreAgentService
(synq.agent.sre.v1.SreAgentService)
#### Connect
Connect ([ConnectRequest](#connectrequest)) [ConnectResponse](#connectresponse)
buf:lint:ignore RPC\_NO\_CLIENT\_STREAMING
#### GetConfig
GetConfig ([GetConfigRequest](#getconfigrequest)) [GetConfigResponse](#getconfigresponse)
GetConfig returns the Scout configuration (system prompts + DWH connection
summary) for the caller's workspace. Authorized for the agent itself
(SCOPE\_SRE\_AGENT), for any caller with workspace integration-read access
(SCOPE\_INTEGRATION\_READ) so admins / owners can inspect the live prompts
via synqctl / fe-app without an agent token, and for MCP read tokens
(SCOPE\_MCP\_READ) — matches the sibling DWH ConnectionsService scope shape.
#### ListPromptSuggestions
ListPromptSuggestions ([ListPromptSuggestionsRequest](#listpromptsuggestionsrequest)) [ListPromptSuggestionsResponse](#listpromptsuggestionsresponse)
ListPromptSuggestions returns prompt-improvement suggestions recorded for the
caller's workspace. Default scope is pending suggestions only; set statuses to
include accepted / dismissed / superseded rows. Sorted newest first.
#### AcceptPromptSuggestion
AcceptPromptSuggestion ([AcceptPromptSuggestionRequest](#acceptpromptsuggestionrequest)) [AcceptPromptSuggestionResponse](#acceptpromptsuggestionresponse)
AcceptPromptSuggestion applies a pending suggestion to the matching SRE agent
prompt field. The applied value is `suggested_value` unless `edited_value` is set.
Returns FAILED\_PRECONDITION when the live prompt has drifted from the suggestion's
`baseline_value` since it was produced — the client should re-fetch and re-render.
#### DismissPromptSuggestion
DismissPromptSuggestion ([DismissPromptSuggestionRequest](#dismisspromptsuggestionrequest)) [DismissPromptSuggestionResponse](#dismisspromptsuggestionresponse)
DismissPromptSuggestion marks a pending suggestion as dismissed with an optional
free-text reason. The SRE agent configuration is left untouched.
#### RecordPromptSuggestion
RecordPromptSuggestion ([RecordPromptSuggestionRequest](#recordpromptsuggestionrequest)) [RecordPromptSuggestionResponse](#recordpromptsuggestionresponse)
RecordPromptSuggestion is the producer-side endpoint used by the offline
analyser to register a new suggestion for a workspace. The server snapshots
the current value of the targeted prompt field as `baseline_value`. Any
pending suggestion for the same `(workspace, field)` pair is marked as
superseded before the new row is inserted. Returns the recorded suggestion,
or an empty response when the baseline already matches `suggested_value`
(no-op).
Authorized for the agent / offline analyser (SCOPE\_SRE\_AGENT) and for
admins / owners with workspace integration-edit access
(SCOPE\_INTEGRATION\_EDIT), so an admin running synqctl can publish a
suggestion against a customer workspace via x-synq-workspace-override
without minting an agent token.
### TriageService
(synq.agent.sre.v1.TriageService)
Service for managing issue triage operations, allowing LLM agents to conclude investigations
and record evidence during the triage process.
#### Conclude
Conclude ([ConcludeRequest](#concluderequest)) [ConcludeResponse](#concluderesponse)
Concludes the triage investigation for one or more issues by providing a structured conclusion
and supporting evidence. This method is used by the LLM agent to finalize its investigation.
#### RecordEvidence
RecordEvidence ([RecordEvidenceRequest](#recordevidencerequest)) [RecordEvidenceResponse](#recordevidenceresponse)
Records evidence collected during the triage investigation. This method allows the LLM agent
to incrementally add evidence as it discovers information during the investigation process.
#### BatchLatestConclusions
BatchLatestConclusions ([BatchLatestConclusionsRequest](#batchlatestconclusionsrequest)) [BatchLatestConclusionsResponse](#batchlatestconclusionsresponse)
Retrieves the latest triage state for one or more issues. This method is used by the LLM agent
to retrieve the current state of the issues it is working on.
#### BatchTriageState
BatchTriageState ([BatchTriageStateRequest](#batchtriagestaterequest)) [BatchTriageStateResponse](#batchtriagestateresponse)
Retrieves the triage state for one or more issues. This method is used by the LLM agent
to retrieve the current state of the issues it is working on.
#### ListTriageState
ListTriageState ([ListTriageStateRequest](#listtriagestaterequest)) [ListTriageStateResponse](#listtriagestateresponse)
Retrieves the triage state for one or more issues. This method is used by the LLM agent
to retrieve the current state of the issues it is working on.
#### MarkAsInProgress
MarkAsInProgress ([MarkAsInProgressRequest](#markasinprogressrequest)) [MarkAsInProgressResponse](#markasinprogressresponse)
Marks an issue as in progress. This method is used by the LLM agent to indicate that it has started working on an issue.
#### MarkFailed
MarkFailed ([MarkFailedRequest](#markfailedrequest)) [MarkFailedResponse](#markfailedresponse)
Marks an issue as failed triaging. This method is used by the LLM agent to indicate that it was unable to triage.
### FeatureRequestService
(synq.agent.sre.v1.FeatureRequestService)
FeatureRequestService allows MCP clients to submit feature requests when users encounter
missing capabilities. This is a last-resort service — it should only be used when no
existing tool can fulfill the user's request.
#### SubmitFeatureRequest
SubmitFeatureRequest ([SubmitFeatureRequestRequest](#submitfeaturerequestrequest)) [SubmitFeatureRequestResponse](#submitfeaturerequestresponse)
SubmitFeatureRequest submits a feature request to the Coalesce Quality product team.
This method should only be called as a last resort when the user's request cannot be
fulfilled by any existing tool. It logs the request for product team review.
### TestsSuggestionStateService
(synq.agent.sre.v1.TestsSuggestionStateService)
#### SuggestFor
SuggestFor ([SuggestForRequest](#suggestforrequest)) [SuggestForResponse](#suggestforresponse)
#### GetActiveSuggestionStatesFor
GetActiveSuggestionStatesFor ([GetActiveSuggestionStatesForRequest](#getactivesuggestionstatesforrequest)) [GetActiveSuggestionStatesForResponse](#getactivesuggestionstatesforresponse)
#### BatchSuggestionStates
BatchSuggestionStates ([BatchSuggestionStatesRequest](#batchsuggestionstatesrequest)) [BatchSuggestionStatesResponse](#batchsuggestionstatesresponse)
#### ListSuggestionStates
ListSuggestionStates ([ListSuggestionStatesRequest](#listsuggestionstatesrequest)) [ListSuggestionStatesResponse](#listsuggestionstatesresponse)
#### MarkAsRunning
MarkAsRunning ([MarkAsRunningRequest](#markasrunningrequest)) [MarkAsRunningResponse](#markasrunningresponse)
#### MarkAsCompleted
MarkAsCompleted ([MarkAsCompletedRequest](#markascompletedrequest)) [MarkAsCompletedResponse](#markascompletedresponse)
#### MarkAsFailed
MarkAsFailed ([MarkAsFailedRequest](#markasfailedrequest)) [MarkAsFailedResponse](#markasfailedresponse)
#### MarkAsCancelled
MarkAsCancelled ([MarkAsCancelledRequest](#markascancelledrequest)) [MarkAsCancelledResponse](#markascancelledresponse)
### ConnectionsService
(synq.agent.dwh.v1.ConnectionsService)
ConnectionsService provides methods for managing data warehouse connections.
These connections represent data warehouse integrations configured in Coalesce Quality that can be used by:
* DWH Agent: For metadata extraction and data quality monitoring
* Coalesce Quality Scout: On-premise AI agent for data exploration and analysis
Both agents share the same or very similar configuration file format.
#### ListConnections
ListConnections ([ListConnectionsRequest](#listconnectionsrequest)) [ListConnectionsResponse](#listconnectionsresponse)
ListConnections retrieves data warehouse connections for a workspace.
Converts Coalesce Quality integration configurations to a standardized connection format.
Requires workspace context in the request metadata.
#### GenerateConnectionsYaml
GenerateConnectionsYaml ([GenerateConnectionsYamlRequest](#generateconnectionsyamlrequest)) [GenerateConnectionsYamlResponse](#generateconnectionsyamlresponse)
GenerateConnectionsYaml converts data warehouse connections to YAML configuration format.
The generated YAML can be used directly as a config file for DWH Agent or Coalesce Quality Scout.
Secret fields are replaced with environment variable placeholders (e.g., `${CONNECTION_ID_PASSWORD}`).
Documentation: [https://docs.synq.io/dw-integrations/agent#config-file-schema](https://docs.synq.io/dw-integrations/agent#config-file-schema)
### DwhAgentService
(synq.agent.dwh.v1.DwhAgentService)
#### Connect
Connect ([ConnectRequest](#connectrequest)) [ConnectResponse](#connectresponse)
buf:lint:ignore RPC\_NO\_CLIENT\_STREAMING
### YamlService
(synq.agent.recon.v1.YamlService)
YamlService provides stateless conversion between YAML config format and proto.
This is useful for UI editors that need to display/edit suite configs as YAML.
#### ParseYaml
ParseYaml ([ParseYamlRequest](#parseyamlrequest)) [ParseYamlResponse](#parseyamlresponse)
ParseYaml parses a YAML config string into a ReconciliationSuite proto.
Only the suite configuration fields are extracted (connections are ignored).
ParseYaml refuses two types of suite with INVALID\_ARGUMENT and a
SuiteValidationFailure detail:
* A suite with `setup_file` or `teardown_file`. These fields refer to
files that the server cannot read.
* A suite with `reporting.level: detailed`.
To find all other problems in a suite, use ValidateSuite.
#### RenderYaml
RenderYaml ([RenderYamlRequest](#renderyamlrequest)) [RenderYamlResponse](#renderyamlresponse)
RenderYaml renders a ReconciliationSuite proto as a YAML config string.
#### ValidateSuite
ValidateSuite ([ValidateSuiteRequest](#validatesuiterequest)) [ValidateSuiteResponse](#validatesuiteresponse)
ValidateSuite checks a suite and gives all the problems that it finds.
Each problem has the path of its field. ValidateSuite does not save the
suite, so an editor can call it after each change.
ValidateSuite does the same checks as `synq-recon check-config` without
`--db`. It also does the checks that apply only to suites that run in
Coalesce Quality.
ValidateSuite does not connect to a warehouse. Thus it does not find a
query that refers to a table that does not exist.
ValidateSuite does not compare the connection names with the integrations
of the workspace. A suite gets its integrations when it runs or when you
promote it.
### SuiteConfigService
(synq.agent.recon.v1.SuiteConfigService)
SuiteConfigService manages reconciliation suite configurations.
Managed suites are long-lived, optionally scheduled configs identified by suite\_name.
Ad-hoc suites are one-off configs that get a generated UUID as suite\_id.
#### UpsertSuiteConfig
UpsertSuiteConfig ([UpsertSuiteConfigRequest](#upsertsuiteconfigrequest)) [UpsertSuiteConfigResponse](#upsertsuiteconfigresponse)
Upsert a managed suite config. Creates if new, updates with new version if exists.
The suite\_id equals the suite name (from ReconciliationSuite.name).
Validates the ReconciliationSuite proto. Does NOT validate connections exist.
A suite with `reporting.level: detailed` gets INVALID\_ARGUMENT with a
SuiteValidationFailure detail. To find all other problems before you save,
call YamlService.ValidateSuite.
Use expected\_state\_at or create\_only to save only when a condition is
true. Without them, the save replaces the suite that is stored.
#### SubmitAdhocSuite
SubmitAdhocSuite ([SubmitAdhocSuiteRequest](#submitadhocsuiterequest)) [SubmitAdhocSuiteResponse](#submitadhocsuiteresponse)
Submit an ad-hoc suite for one-off execution.
Provide either an inline suite or a suite\_id referencing an existing managed suite.
Optionally apply environment overrides before execution.
The client-provided invocation\_id ensures idempotency.
#### GetSuiteConfig
GetSuiteConfig ([GetSuiteConfigRequest](#getsuiteconfigrequest)) [GetSuiteConfigResponse](#getsuiteconfigresponse)
Get the latest version of a suite config by suite\_id.
Works for both managed (suite\_id = suite\_name) and ad-hoc (suite\_id = UUID) configs.
#### ListSuiteConfigs
ListSuiteConfigs ([ListSuiteConfigsRequest](#listsuiteconfigsrequest)) [ListSuiteConfigsResponse](#listsuiteconfigsresponse)
List latest suite configs with filtering and pagination.
By default returns only managed suites. Set include\_adhoc to also include ad-hoc configs.
#### DeleteSuiteConfig
DeleteSuiteConfig ([DeleteSuiteConfigRequest](#deletesuiteconfigrequest)) [DeleteSuiteConfigResponse](#deletesuiteconfigresponse)
Delete a managed suite config (soft delete — inserts a deleted version).
#### ListSuiteConfigVersions
ListSuiteConfigVersions ([ListSuiteConfigVersionsRequest](#listsuiteconfigversionsrequest)) [ListSuiteConfigVersionsResponse](#listsuiteconfigversionsresponse)
Get version history for a suite config.
#### GetSuiteConfigVersion
GetSuiteConfigVersion ([GetSuiteConfigVersionRequest](#getsuiteconfigversionrequest)) [GetSuiteConfigVersionResponse](#getsuiteconfigversionresponse)
Get a specific historical version of a suite config.
### AuditLogService
(synq.agent.recon.v1.AuditLogService)
AuditLogService provides methods to ingest and query reconciliation audit logs.
#### IngestAuditLog
IngestAuditLog ([IngestAuditLogRequest](#ingestauditlogrequest)) [IngestAuditLogResponse](#ingestauditlogresponse)
IngestAuditLog stores a reconciliation run audit log.
Reporting a locally executed run is part of running one, so the scope that
authorises editing and running suites also authorises reporting their
results — a pipeline needs one credential, not two.
#### ListAuditLogs
ListAuditLogs ([ListAuditLogsRequest](#listauditlogsrequest)) [ListAuditLogsResponse](#listauditlogsresponse)
ListAuditLogs returns audit logs ordered from newest to oldest, optionally filtered by suite and time range.
#### GetAuditLog
GetAuditLog ([GetAuditLogRequest](#getauditlogrequest)) [GetAuditLogResponse](#getauditlogresponse)
GetAuditLog returns a single audit log by invocation ID.
### SuiteDeploymentService
(synq.agent.recon.v1.SuiteDeploymentService)
SuiteDeploymentService is the platform plane of reconciliation.
SuiteConfigService is the developer/authoring sandbox (edit, version, run
ad-hoc with your own credentials — preview-only, no Entity, no Run, no
Issue). A deployment is what SYNQ actually runs on the workspace's behalf:
a frozen snapshot of a suite config, mapped to workspace-level SYNQ
integrations, optionally scheduled or triggerable by API.
Identity: every deployment has a stable deployment\_id (UUIDv7) assigned on
first promote. It survives re-promotes, schedule/mapping changes, and
pause/resume. Downstream AssetCommand / RunCommand publication keys off
deployment\_id so Entity identity does not fragment on promote churn.
#### PromoteSuite
PromoteSuite ([PromoteSuiteRequest](#promotesuiterequest)) [PromoteSuiteResponse](#promotesuiteresponse)
Promote a suite config into production. Freezes a snapshot of the config
and stores the connection → integration mapping. Creates a new deployment
on first call; re-promotes the same suite\_id (reusing its deployment\_id)
on subsequent calls.
#### UnpromoteSuite
UnpromoteSuite ([UnpromoteSuiteRequest](#unpromotesuiterequest)) [UnpromoteSuiteResponse](#unpromotesuiteresponse)
Unpromote a deployment. Marks it inactive; scheduler and API triggers
stop. Deployment history is preserved; re-promoting the same suite\_id
reuses the original deployment\_id.
#### UpdateDeploymentSchedule
UpdateDeploymentSchedule ([UpdateDeploymentScheduleRequest](#updatedeploymentschedulerequest)) [UpdateDeploymentScheduleResponse](#updatedeploymentscheduleresponse)
Change schedule and/or triggerable\_by\_api on an existing deployment
without re-promoting the config snapshot or touching mappings.
#### UpdateDeploymentMappings
UpdateDeploymentMappings ([UpdateDeploymentMappingsRequest](#updatedeploymentmappingsrequest)) [UpdateDeploymentMappingsResponse](#updatedeploymentmappingsresponse)
Rewire connection → integration mappings on an existing deployment
without re-promoting the config snapshot.
#### UpdateDeploymentAnnotations
UpdateDeploymentAnnotations ([UpdateDeploymentAnnotationsRequest](#updatedeploymentannotationsrequest)) [UpdateDeploymentAnnotationsResponse](#updatedeploymentannotationsresponse)
Replace the deployment-level annotation overlay on an existing
deployment. Annotations are merged with suite-level and case-level
annotations from the frozen suite snapshot at promote/update time, then
surfaced on PromotedReconSuiteMetadata / PromotedReconCaseMetadata for
every asset under the deployment.
#### UpdateDeploymentCategories
UpdateDeploymentCategories ([UpdateDeploymentCategoriesRequest](#updatedeploymentcategoriesrequest)) [UpdateDeploymentCategoriesResponse](#updatedeploymentcategoriesresponse)
Change the categories declared for a deployment's reconciliation checks
without re-promoting the suite snapshot. Each field is patched independently:
omitting one leaves it as it was, sending an empty string clears it, and
sending a value sets or replaces it — so changing the governance category can
never disturb the technical one.
The new categories reach the published checks immediately; a re-promote is not
needed for them to take effect.
#### UpdateDeploymentCaseImpacts
UpdateDeploymentCaseImpacts ([UpdateDeploymentCaseImpactsRequest](#updatedeploymentcaseimpactsrequest)) [UpdateDeploymentCaseImpactsResponse](#updatedeploymentcaseimpactsresponse)
Patch the per-case ReconciliationFailureImpact map on an existing deployment
without re-promoting the suite snapshot. Useful for "apply to all"
FE actions and one-off toggles between RAISE\_ISSUE\_ON\_TABLES and
SHOW\_ON\_TABLES. Re-renders PromotedReconCaseMetadata so the
error\_tables / shown\_tables split reflects the new policy
immediately.
Merge semantics: keys present in the request overwrite the stored
value for that case. Keys absent from the request are left
untouched (no implicit reset). Sending RECONCILIATION\_FAILURE\_IMPACT\_UNSPECIFIED
clears the override for that case (falls back to the default
RAISE\_ISSUE\_ON\_TABLES). Unknown case names are rejected with
INVALID\_ARGUMENT so the FE catches typos before the deployment is
mutated.
#### PauseDeployment
PauseDeployment ([PauseDeploymentRequest](#pausedeploymentrequest)) [PauseDeploymentResponse](#pausedeploymentresponse)
Pause a deployment. Scheduled and API-triggered runs are suppressed until
`until` is reached, or indefinitely when `until` is absent.
#### ResumeDeployment
ResumeDeployment ([ResumeDeploymentRequest](#resumedeploymentrequest)) [ResumeDeploymentResponse](#resumedeploymentresponse)
Resume a paused deployment.
#### GetSuiteDeployment
GetSuiteDeployment ([GetSuiteDeploymentRequest](#getsuitedeploymentrequest)) [GetSuiteDeploymentResponse](#getsuitedeploymentresponse)
Fetch a deployment by suite\_id or deployment\_id.
#### ListSuiteDeployments
ListSuiteDeployments ([ListSuiteDeploymentsRequest](#listsuitedeploymentsrequest)) [ListSuiteDeploymentsResponse](#listsuitedeploymentsresponse)
List deployments for the caller's workspace. Filters support answering
"what is SYNQ running on behalf of this workspace?" and "which
deployments use this SYNQ integration?".
#### ListSuiteDeploymentHistory
ListSuiteDeploymentHistory ([ListSuiteDeploymentHistoryRequest](#listsuitedeploymenthistoryrequest)) [ListSuiteDeploymentHistoryResponse](#listsuitedeploymenthistoryresponse)
List the append-only history of a deployment (every promote,
re-promote, schedule/mapping change, pause/resume, unpromote).
#### TriggerDeployment
TriggerDeployment ([TriggerDeploymentRequest](#triggerdeploymentrequest)) [TriggerDeploymentResponse](#triggerdeploymentresponse)
Trigger an on-demand execution of a promoted deployment. Enqueues a run
using the frozen suite snapshot and connection mappings stored on the
deployment row — execution uses platform integration credentials, not the
caller's. Returns the freshly-allocated `run_id` so callers can poll
RunStateService for status.
Preconditions:
* Deployment must exist and be `active`.
* Deployment must have `triggerable_by_api = true` (FAILED\_PRECONDITION
otherwise).
* Deployment must not be paused (FAILED\_PRECONDITION when
`paused_until` is set and in the future).
### RunStateService
(synq.agent.recon.v1.RunStateService)
RunStateService exposes lifecycle status of reconciliation runs.
Polling-based read API — clients fetch the current state for one or more
suites and display progress. No notifications are emitted; clients re-poll
to observe transitions.
#### BatchReconciliationRunState
BatchReconciliationRunState ([BatchReconciliationRunStateRequest](#batchreconciliationrunstaterequest)) [BatchReconciliationRunStateResponse](#batchreconciliationrunstateresponse)
BatchReconciliationRunState returns the latest concluded run and any ongoing
(SCHEDULED or IN\_PROGRESS) run for each suite\_id. Returned map is keyed by suite\_id.
Suites with no recorded runs are omitted from the response map.
#### ListReconciliationRunState
ListReconciliationRunState ([ListReconciliationRunStateRequest](#listreconciliationrunstaterequest)) [ListReconciliationRunStateResponse](#listreconciliationrunstateresponse)
ListReconciliationRunState returns runs filtered by status and/or suite\_id.
Sorted newest-first by scheduled\_at.
#### CancelReconciliationRun
CancelReconciliationRun ([CancelReconciliationRunRequest](#cancelreconciliationrunrequest)) [CancelReconciliationRunResponse](#cancelreconciliationrunresponse)
CancelReconciliationRun marks a SCHEDULED or IN\_PROGRESS run as
CANCELLED so the suite is no longer "occupied" by a stuck occurrence
— used to recover from local-dev / executor outages where a run was
queued but never executed. Already-terminal runs are returned
unchanged. Idempotent: calling on an already-cancelled run is a
no-op.
Note: this only updates the run-state row. The corresponding
execution-job row is independent — when an executor eventually
wakes up to that job it will see the run is CANCELLED and skip.
### ChangesService
(synq.entities.changes.v1.ChangesService)
ChangesService provides functionality to track and retrieve all types of changes to data entities.
This unified service returns ALL change types for an entity:
* Git commits: Changes to code files (dbt models, SQL files) tracked in version control
* Schema changes: Database schema modifications (columns added/removed/changed)
* SQL definition changes: View/materialized view definition updates detected by Coalesce Quality
Changes are returned with structured metadata including:
* For git commits: structured statistics (directories, file types, top changes)
* For schema changes: detailed column-level diffs
* For SQL changes: before/after SQL definitions
Use cases:
* "What changed in the last week for table X?" → Returns git commits, schema changes, SQL changes
* "Show me all commits affecting this dbt model" → Returns git commits with lineage context
* "What schema changes happened to this table?" → Returns schema changes detected by Coalesce Quality
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
Retrieves the complete change history for specified entities.
Returns all change types (git commits, schema changes, SQL changes) in chronological order (newest first).
Changes include lineage context showing upstream\_distance when changes affect dependencies.
#### BatchChange
BatchChange ([BatchChangeRequest](#batchchangerequest)) [BatchChangeResponse](#batchchangeresponse)
BatchChange retrieves multiple entity changes by their IDs in a single request.
This is an optimized endpoint for fetching specific changes when you already know their IDs,
rather than listing changes for an entity.
### SchemaMismatchesService
(synq.entities.schemas.v1.SchemaMismatchesService)
SchemaMismatchesService provides access to schema drift information between
data platform tables and their definitions (e.g., dbt models).
#### BatchSchemaMismatches
BatchSchemaMismatches ([BatchSchemaMismatchesRequest](#batchschemamismatchesrequest)) [BatchSchemaMismatchesResponse](#batchschemamismatchesresponse)
Get schema mismatches for a batch of entities by their identifiers.
### SchemasService
(synq.entities.schemas.v1.SchemasService)
EntitiesService is a service for retriving any entity.
#### GetSchema
GetSchema ([GetSchemaRequest](#getschemarequest)) [GetSchemaResponse](#getschemaresponse)
Get schema of the entity its identifier.
### TableConstraintsService
(synq.entities.constraints.v1.TableConstraintsService)
TableConstraintsService provides access to table constraint and index information.
#### GetTableConstraints
GetTableConstraints ([GetTableConstraintsRequest](#gettableconstraintsrequest)) [GetTableConstraintsResponse](#gettableconstraintsresponse)
Get table constraints (primary keys, foreign keys, indexes, etc.) for a given entity.
### ChecksRelationshipsService
(synq.entities.custom.v1.ChecksRelationshipsService)
#### UpsertCheckRelationships
UpsertCheckRelationships ([UpsertCheckRelationshipsRequest](#upsertcheckrelationshipsrequest)) [UpsertCheckRelationshipsResponse](#upsertcheckrelationshipsresponse)
Create or update relationships that attach a check (e.g. a SQL test) to the
entities it validates. Idempotent per (check, checked) pair: an existing
relationship is updated, otherwise it is created.
The response reports what each requested relationship did, and under which
(check, checked) pair of entities it was stored.
#### DeleteCheckRelationships
DeleteCheckRelationships ([DeleteCheckRelationshipsRequest](#deletecheckrelationshipsrequest)) [DeleteCheckRelationshipsResponse](#deletecheckrelationshipsresponse)
Delete relationships between checks and the entities they validate.
Relationships that do not exist are ignored.
The response says which of them actually removed something.
#### ListCheckRelationships
ListCheckRelationships ([ListCheckRelationshipsRequest](#listcheckrelationshipsrequest)) [ListCheckRelationshipsResponse](#listcheckrelationshipsresponse)
List every relationship that attaches a check to the entities it
validates, across the whole workspace.
### EntitiesService
(synq.entities.custom.v1.EntitiesService)
custom.EntitiesService is a service for managing custom entities. Entities can represent
various data platform concepts such as services, consumers, applications or data pipelines
that are not natively available in Coalesce Quality.
Entities are identified by a unique identifier and can be created, updated, read and deleted.
#### UpsertEntity
UpsertEntity ([UpsertEntityRequest](#upsertentityrequest)) [UpsertEntityResponse](#upsertentityresponse)
Create or update an entity. If the entity does not exist, it will be created, otherwise it will be updated. Entities are identified and deduplicated by their Identifier in a scope of a given customer workspace.
#### DeleteEntity
DeleteEntity ([DeleteEntityRequest](#deleteentityrequest)) [DeleteEntityResponse](#deleteentityresponse)
Delete an entity by its identifier. If the entity does not exist, it will be a no-op.
#### ListEntities
ListEntities ([ListEntitiesRequest](#listentitiesrequest)) [ListEntitiesResponse](#listentitiesresponse)
List all entities.
### RelationshipsService
(synq.entities.custom.v1.RelationshipsService)
RelationshipsService allow management of relationships between entities. Relationships can
be created, updated, and deleted between 2 custom entities, or between a custom entity and Coalesce Quality native entity.enum
There is no option to create relationships between 2 Coalesce Quality native entities (dbt model, BI dashboard, etc.).
#### UpsertRelationships
UpsertRelationships ([UpsertRelationshipsRequest](#upsertrelationshipsrequest)) [UpsertRelationshipsResponse](#upsertrelationshipsresponse)
Create or update relationships between entities. If the relationship already exists, it will be updated, otherwise it will be created.
The response reports what each requested relationship did to the stored
graph, and under which pair of entities it was stored. Read it: a
relationship whose endpoints resolve to a pair another relationship in the
same batch also resolves to is one edge, and only the last write to it
survives.
#### DeleteRelationships
DeleteRelationships ([DeleteRelationshipsRequest](#deleterelationshipsrequest)) [DeleteRelationshipsResponse](#deleterelationshipsresponse)
Delete relationships between entities. If the relationship does not exist, it will be ignored.
The response says which of them actually removed something, so a caller
reconciling a set of edges can tell a withdrawal from a no-op.
#### ListRelationships
ListRelationships ([ListRelationshipsRequest](#listrelationshipsrequest)) [ListRelationshipsResponse](#listrelationshipsresponse)
List relationships touching the given entities, or every relationship in
the workspace when ids is left empty.
### EntityExecutionsService
(synq.entities.custom.v1.EntityExecutionsService)
#### UpsertExecution
UpsertExecution ([UpsertExecutionRequest](#upsertexecutionrequest)) [UpsertExecutionResponse](#upsertexecutionresponse)
Execution affects status of the custom entity
#### UpsertLogEntry
UpsertLogEntry ([UpsertLogEntryRequest](#upsertlogentryrequest)) [UpsertLogEntryResponse](#upsertlogentryresponse)
LogEntry is a log message for the custom entity
### TypesService
(synq.entities.custom.v1.TypesService)
TypesService is a service for managing custom entity types.
#### UpsertType
UpsertType ([UpsertTypeRequest](#upserttyperequest)) [UpsertTypeResponse](#upserttyperesponse)
Create or update an entity. If the entity does not exist, it will be created, otherwise it will be updated. Entities are identified and deduplicated by their Identifier in a scope of a given customer workspace.
#### DeleteType
DeleteType ([DeleteTypeRequest](#deletetyperequest)) [DeleteTypeResponse](#deletetyperesponse)
Delete an entity by its identifier. If the entity does not exist, it will be a no-op.
#### ListTypes
ListTypes ([ListTypesRequest](#listtypesrequest)) [ListTypesResponse](#listtypesresponse)
List all entities.
### FeaturesService
(synq.entities.custom.v1.FeaturesService)
#### UpsertEntityFeature
UpsertEntityFeature ([UpsertEntityFeatureRequest](#upsertentityfeaturerequest)) [UpsertEntityFeatureResponse](#upsertentityfeatureresponse)
Create or update an entity. If the entity does not exist, it will be created, otherwise it will be updated. Entities are identified and deduplicated by their Identifier in a scope of a given customer workspace.
#### DeleteEntityFeature
DeleteEntityFeature ([DeleteEntityFeatureRequest](#deleteentityfeaturerequest)) [DeleteEntityFeatureResponse](#deleteentityfeatureresponse)
Delete an entity by its identifier. If the entity does not exist, it will be a no-op.
#### ListEntityFeatures
ListEntityFeatures ([ListEntityFeaturesRequest](#listentityfeaturesrequest)) [ListEntityFeaturesResponse](#listentityfeaturesresponse)
List the features of one entity, or of every custom entity in the
workspace when entity\_id is left unset.
### GroupsService
(synq.entities.custom.v1.GroupsService)
It eliminates the need to keep state on client side to remember which assets were already created
and which should be deleted. The server will keep track of the current state of the group and client
can always send the intended new state. The server will calculate the diff and entities that are
no longer present in the group will be removed.
Example:
1. group has entities A, B, C at time t1
2. client sends group with entities B, C, D at time t2
3. server will remove entity A from the system and update the current state of the group to B, C, D
The service is designed to be idempotent and can be called multiple times with the same state without
causing any side effects.
#### ListEntityGroups
ListEntityGroups ([ListEntityGroupsRequest](#listentitygroupsrequest)) [ListEntityGroupsResponse](#listentitygroupsresponse)
ListEntityGroups lists all groups.
#### UpsertEntitiesGroup
UpsertEntitiesGroup ([UpsertEntitiesGroupRequest](#upsertentitiesgrouprequest)) [UpsertEntitiesGroupResponse](#upsertentitiesgroupresponse)
UpsertEntitiesGroup updates current definition of group to the server. As part of the upsert operation
the difference of entity ids is calculated between past state and the current state and entities that are
no longer part of the group are removed.
#### DeleteEntitiesGroup
DeleteEntitiesGroup ([DeleteEntitiesGroupRequest](#deleteentitiesgrouprequest)) [DeleteEntitiesGroupResponse](#deleteentitiesgroupresponse)
DeleteEntitiesGroup deletes a group by its identifier. If the group does not exist, it will be a no-op.
### FoldersService
(synq.entities.folders.v1.FoldersService)
FoldersService discovers and browses the folder / containment hierarchy of the
catalog: the roots data hangs from, the sub-folders under any folder, the
folders a given entity lives in, and the entities contained in a folder.
It is a read / navigation surface, not folder management — there are no
create, move, or delete operations.
Addressing: every folder has an opaque `folder_entity_id` (round-trip it
verbatim) and a structured `FolderRef{root_entity_id, folders[]}` (the
human-friendly form). Entities — both folder members and the ids you pass to
`GetFolderOf` — are named by their opaque entity id, the same id used across
the rest of the entities API.
Depth footgun: membership is SHALLOW by default. `Browse` returns only the
direct children of a folder, and `ListMembers` returns only the entities
placed directly in the folder. Set `deep = true` (or raise `Browse.depth`) to
recurse into sub-folders.
#### Browse
Browse ([BrowseRequest](#browserequest)) [BrowseResponse](#browseresponse)
Browse the hierarchy one level at a time. With no `parent`, returns the
roots (top-level integrations / containers). With a `parent` folder, returns
that folder's direct sub-folders and the entity ids of its direct,
non-folder members.
One level per call — sub-folders are returned as `Folder`s (each with a
`child_folder_count`), which you Browse into in turn; there is no nested
tree in the response. `filter` narrows the returned members to entities
matching a query.
#### GetFolderOf
GetFolderOf ([GetFolderOfRequest](#getfolderofrequest)) [GetFolderOfResponse](#getfolderofresponse)
Given entity ids, return the folder breadcrumb(s) each entity lives in —
ordered root first, leaf (immediate parent) last. An entity may appear in
more than one hierarchy; the response carries every folder along its
containing chain. Compose with `ListMembers` on an entity's immediate parent
to list its siblings.
#### ListMembers
ListMembers ([ListMembersRequest](#listmembersrequest)) [ListMembersResponse](#listmembersresponse)
List the entity ids contained in a folder, paginated. Direct members only by
default; set `deep = true` to include members of all nested sub-folders.
Narrow with `types` and/or `filter`.
Pagination is a stable client-side window over the folder's fully-resolved
member set: each page recomputes and re-orders that set, then returns the
requested slice. Results are consistent, but paging deep through a very
large folder repeats work — prefer a larger `page_size` over many small
pages.
### AnnotationsService
(synq.entities.annotations.v1.AnnotationsService)
AnnotationsService provides operations for managing and querying entity annotations.
Annotations are key-value pairs that can be attached to entities for categorization and filtering.
#### ListAnnotations
ListAnnotations ([ListAnnotationsRequest](#listannotationsrequest)) [ListAnnotationsResponse](#listannotationsresponse)
List all available annotations with their usage counts across entities.
#### BatchGetEntityAnnotations
BatchGetEntityAnnotations ([BatchGetEntityAnnotationsRequest](#batchgetentityannotationsrequest)) [BatchGetEntityAnnotationsResponse](#batchgetentityannotationsresponse)
Read the annotations carried by specific entities.
ListAnnotations answers what the workspace annotates by and how often;
this answers what one entity carries. They are separate reads because an
entity's annotations are not part of the entity: `synq.entities.v1.Entity`
is the shape `synq.entities.custom.v1.UpsertEntity` accepts, so the
`annotations` field on it is what a producer ASSERTS about an entity, and
answering a read with it would make a read-edit-write round trip write one
entity's annotations onto another.
### EntityIssuesService
(synq.entities.status.v1.EntityIssuesService)
EntityIssuesService is the service which retrieves entity issues status.
#### GetIssuesStatus
GetIssuesStatus ([GetIssuesStatusRequest](#getissuesstatusrequest)) [GetIssuesStatusResponse](#getissuesstatusresponse)
Get specific entity status by its identifier.
#### BatchGetIssuesStatus
BatchGetIssuesStatus ([BatchGetIssuesStatusRequest](#batchgetissuesstatusrequest)) [BatchGetIssuesStatusResponse](#batchgetissuesstatusresponse)
Get multiple entity statuses by their identifiers.
### EntityIncidentsService
(synq.entities.status.v1.EntityIncidentsService)
EntityIncidentsService is the service which retrieves entity status.
#### GetIncidents
GetIncidents ([GetIncidentsRequest](#getincidentsrequest)) [GetIncidentsResponse](#getincidentsresponse)
Get specific entity status by its identifier.
#### BatchGetIncidents
BatchGetIncidents ([BatchGetIncidentsRequest](#batchgetincidentsrequest)) [BatchGetIncidentsResponse](#batchgetincidentsresponse)
Get multiple entity statuses by their identifiers.
### ImpactService
(synq.entities.impact.v1.ImpactService)
#### GetImpact
GetImpact ([GetImpactRequest](#getimpactrequest)) [GetImpactResponse](#getimpactresponse)
### DatabaseCoordinatesService
(synq.entities.coordinates.v1.DatabaseCoordinatesService)
DatabaseCoordinatesService is a service for getting database coordinates of Entities.
#### BatchDatabaseCoordinates
BatchDatabaseCoordinates ([BatchDatabaseCoordinatesRequest](#batchdatabasecoordinatesrequest)) [BatchDatabaseCoordinatesResponse](#batchdatabasecoordinatesresponse)
Get multiple DatabaseCoordinates for identifiers.
#### BatchIdsByCoordinates
BatchIdsByCoordinates ([BatchIdsByCoordinatesRequest](#batchidsbycoordinatesrequest)) [BatchIdsByCoordinatesResponse](#batchidsbycoordinatesresponse)
Given database FQN return possible entity ids.
### ActivityService
(synq.entities.activity.v1alpha.ActivityService)
ActivityService reads the query history of a workspace's data platforms: how popular an entity is,
who reads and writes it, what one login, tool, job or person touched and what it cost, and where a
workspace's compute goes.
Every read covers a window of whole UTC hours, by default the 30 days before the current hour. A
login's reads count toward popularity as its service account's popularity policy says (see
synq.entities.platformusers.v1alpha.ServiceAccountsService), and Coalesce Quality's own queries never
count.
Weights are per query, in each platform's own figures. bytes\_scanned is never a cost, and no
platform reports cost per table, so an entity's share of a query is an equal split. Nothing here is
credits or currency.
#### BatchGetPopularity
BatchGetPopularity ([BatchGetPopularityRequest](#batchgetpopularityrequest)) [BatchGetPopularityResponse](#batchgetpopularityresponse)
Read how popular each asked entity is over the window: its counted reads and writes per measure,
and its rank in each pool it has reads in. An entity is answered with the entities it is the same
as, such as a dbt model and the table it builds. Every asked id has an answer, an empty one when
nothing counted.
#### ListEntityActors
ListEntityActors ([ListEntityActorsRequest](#listentityactorsrequest)) [ListEntityActorsResponse](#listentityactorsresponse)
Read who read and wrote each asked entity over the window: the jobs, the service accounts, the
tools and the logins. Anyone who reads lineage, and the reliability agent, is served. Only a caller holding the platform users
scope sees the logins: any other gets every logins list empty and logins\_hidden set.
#### ListPersonActivity
ListPersonActivity ([ListPersonActivityRequest](#listpersonactivityrequest)) [ListPersonActivityResponse](#listpersonactivityresponse)
Read what one person's logins read and wrote over the window, per entity, direction and measure.
What a colleague read is personal data, so a user caller needs the platform users scope from its
role, which a Developer and an Admin have. Logins in a service account, and Coalesce Quality's
own queries, are never part of the answer.
#### GetEntityActivitySeries
GetEntityActivitySeries ([GetEntityActivitySeriesRequest](#getentityactivityseriesrequest)) [GetEntityActivitySeriesResponse](#getentityactivityseriesresponse)
Read how access to one entity moves over the window: per direction, a point per UTC hour, a point
per day in the asked time zone, and a weekday and hour grid. A direction's total is the sum of its
actors in ListEntityActors under the same lens. It names tools, never a login.
#### BatchGetEntityUsage
BatchGetEntityUsage ([BatchGetEntityUsageRequest](#batchgetentityusagerequest)) [BatchGetEntityUsageResponse](#batchgetentityusageresponse)
Read how each asked entity is used over the window, in one line: what loads it, what runs and
builds it, what else writes it, and who reads it, such as "loaded by Fivetran, orchestrated by
Airflow, transformed by dbt, read by Looker Studio, 3 people and 12 logins". Each role names its actor at
the most precise level known: a job or app, else a service account, else the tool. It answers
"what runs this" for a table or a model, and GetActorSummary answers "what this job runs". It
names tools, jobs and service accounts, never a login.
#### GetActorSummary
GetActorSummary ([GetActorSummaryRequest](#getactorsummaryrequest)) [GetActorSummaryResponse](#getactorsummaryresponse)
Read what one actor read, built and wrote over the window, what its queries cost per compute and
platform, and when it works. The actor is asked for by name, so nothing is left out for being
Coalesce Quality's own access, a writer's own read or a policy.
What a person did is personal data: a person, or a login, is answered only to that person, or to a
caller holding the platform users scope, which a Developer and an Admin have. Anyone else gets
PERMISSION\_DENIED. A job, a service account, a tool and Coalesce Quality are answered to every
caller, and without the platform users scope the answer names no login.
#### GetActivityOverview
GetActivityOverview ([GetActivityOverviewRequest](#getactivityoverviewrequest)) [GetActivityOverviewResponse](#getactivityoverviewresponse)
Read a workspace's queries and their cost per group of logins, per platform, and its most read
entities. It names groups and entities, never a login or a person.
#### ListActors
ListActors ([ListActorsRequest](#listactorsrequest)) [ListActorsResponse](#listactorsresponse)
List the workspace's actors, the service accounts, tools, people, logins nothing classifies and
Coalesce Quality, each with the totals GetActorSummary has for it: its queries and their weights,
its reads, inputs and writes, and the entities it read and wrote. A login can be in several actors,
such as its service account and the tool that issued its queries, so the actors never add up to
the workspace: GetActivityOverview is the split that does. Only a caller holding the platform users
scope sees people and logins: any other gets the rest with people\_hidden set.
#### ListAccessSignals
ListAccessSignals ([ListAccessSignalsRequest](#listaccesssignalsrequest)) [ListAccessSignalsResponse](#listaccesssignalsresponse)
List the entities of the workspace, or of a selection, that one signal names, such as the tables
a pipeline builds and nobody reads, or the tables nobody touched. A signal is computed from the
window's access on each call, so a service account change shows on the next one. It names
entities and tools, never a login.
### OrchestrationService
(synq.entities.orchestration.v1.OrchestrationService)
OrchestrationService tells which jobs, job steps and tasks run an entity, and which entities a
job, job step or task runs, from the runs of the last 31 days. It covers Airflow tasks and DAGs,
dbt Cloud job steps and Coalesce jobs.
Deprecated: use synq.entities.activity.v1alpha.ActivityService. BatchGetEntityUsage answers "what
runs this" for a table or a model, as its ORCHESTRATED and TRANSFORMED roles, and GetActorSummary
answers "what this job runs", from its RUNS writes.
#### BatchOrchestration
BatchOrchestration ([BatchOrchestrationRequest](#batchorchestrationrequest)) [BatchOrchestrationResponse](#batchorchestrationresponse)
BatchOrchestration returns, for each requested entity, the jobs, job steps and tasks that ran it
in the last 31 days (upstream), and the entities it ran (downstream).
### ChecksCategoriesService
(synq.entities.checks.v2.ChecksCategoriesService)
ChecksCategoriesService reads and edits the categories of the checks in a
workspace.
Categorising checks is what turns a long list of tests into a picture of data
quality: it lets you ask how much of a data product is covered for
completeness, or which freshness checks exist across three warehouses that
each name the same idea differently.
Every check resolves to at most one category per dimension:
* technical — what kind of check this is, mechanically ("nullness",
"uniqueness", "freshness");
* governance — what the check is for, in data-quality terms
("Completeness", "Accuracy", "Timeliness").
The two are independent, and each resolves from three sources, in descending
precedence: a category set explicitly on the check through this service, a
category the producing tool declared, and a category one of the workspace's
categorisation rules computed. Rules are managed by
ChecksCategorisationRulesService and are the right tool for anything that
generalises; set a category explicitly only for the checks that are genuine
exceptions.
The workspace and the acting identity always come from the request
credentials, never from the payload.
#### UpsertCheckCategories
UpsertCheckCategories ([UpsertCheckCategoriesRequest](#upsertcheckcategoriesrequest)) [UpsertCheckCategoriesResponse](#upsertcheckcategoriesresponse)
Set the category of one or more checks explicitly. An explicit category is
authoritative for its check: it overrides both the producer-declared
category and anything the categorisation rules compute, and it survives
every later rule change.
Each dimension updates independently. A field that is omitted leaves that
dimension as it is; a field set to the empty string clears the explicit
category on that dimension, so the check falls back to its
producer-declared or rule-computed value. Repeating the same request
converges to the same state rather than accumulating changes.
#### DeleteCheckCategories
DeleteCheckCategories ([DeleteCheckCategoriesRequest](#deletecheckcategoriesrequest)) [DeleteCheckCategoriesResponse](#deletecheckcategoriesresponse)
Clear the explicit categories of one or more checks, on both dimensions.
Each check falls back to its producer-declared or rule-computed category.
Idempotent — clearing a check that has no explicit category succeeds.
#### ListCheckCategories
ListCheckCategories ([ListCheckCategoriesRequest](#listcheckcategoriesrequest)) [ListCheckCategoriesResponse](#listcheckcategoriesresponse)
List the workspace's checks with their resolved categories. Returns every
check by default; the filters combine, so one call answers questions like
"which dbt tests still have no governance category" or "which checks did
this rule categorise".
#### BatchGetCheckCategories
BatchGetCheckCategories ([BatchGetCheckCategoriesRequest](#batchgetcheckcategoriesrequest)) [BatchGetCheckCategoriesResponse](#batchgetcheckcategoriesresponse)
Read the categories of specific checks. Ids that do not exist are omitted
from the response rather than causing an error, so a partially-stale list
of ids still returns what it can.
#### ListCategories
ListCategories ([ListCategoriesRequest](#listcategoriesrequest)) [ListCategoriesResponse](#listcategoriesresponse)
List the workspace's category vocabulary: the category names in use on
each dimension, whether a workspace or platform rule produces each one, and
how many checks carry it. Use it to populate a category picker so a
workspace converges on one spelling per concept, and to see how much of the
workspace is still uncategorised.
#### ListCategorisationFilters
ListCategorisationFilters ([ListCategorisationFiltersRequest](#listcategorisationfiltersrequest)) [ListCategorisationFiltersResponse](#listcategorisationfiltersresponse)
List the attribute values the workspace's checks actually have — the
platforms, entity types, packages and kinds available to build a
categorisation rule or a listing filter from.
Pass `selected` to narrow each list to values that co-occur with the
current selection, so a rule editor never offers a combination that matches
nothing. A list never constrains itself: choosing one platform still leaves
every other platform selectable.
### ChecksCategorisationRulesService
(synq.entities.checks.v2.ChecksCategorisationRulesService)
ChecksCategorisationRulesService manages the rules that categorise a
workspace's checks automatically.
A rule says "checks matching this predicate get this category on this
dimension". Rules are the scalable way to categorise: they apply to checks
that already exist and to every check ingested afterwards, so a new dbt
package or a new warehouse does not mean a fresh round of manual tagging. A
rule-computed category is the lowest-precedence source — a category set
explicitly on a check through ChecksCategoriesService, and one the producing
tool declared, both win over it.
A typical loop is: ListCategorisationFilters to see what the workspace's
checks look like, PreviewRule to check the reach of a candidate, UpsertRule
to save it, then GetRecategorisationStatus to confirm the existing checks
have caught up.
Every workspace starts with a set of platform-provided rules covering the
common cases. They are visible here, they can be superseded by a workspace
rule of any priority, and they cannot be edited from a workspace.
#### UpsertRule
UpsertRule ([UpsertRuleRequest](#upsertrulerequest)) [UpsertRuleResponse](#upsertruleresponse)
Create or update a categorisation rule.
The rule id is supplied by the caller (a UUID), which makes this operation
idempotent — repeating the same request converges to the same rule rather
than creating a duplicate. On create, `dimension`, `title`, `category` and
`matcher` are required. On update, a field that is set is written and a
field that is omitted is left unchanged. To guard against overwriting a
concurrent edit, pass the `etag` you last read.
Saving a rule schedules a sweep that recategorises the workspace's existing
checks. It normally completes in seconds; GetRecategorisationStatus
confirms it.
#### DeleteRule
DeleteRule ([DeleteRuleRequest](#deleterulerequest)) [DeleteRuleResponse](#deleteruleresponse)
Delete a categorisation rule. Checks the rule categorised fall back to the
next rule that matches them, or become uncategorised. Idempotent —
deleting an already-deleted or unknown rule succeeds. Pass `etag` to guard
against deleting a concurrently-edited rule.
#### ListRules
ListRules ([ListRulesRequest](#listrulesrequest)) [ListRulesResponse](#listrulesresponse)
List the rules that apply to the caller's workspace — its own rules plus
the platform-provided ones — in the order they are tried. Narrow with
`dimension` and `scope`.
#### BatchGetRules
BatchGetRules ([BatchGetRulesRequest](#batchgetrulesrequest)) [BatchGetRulesResponse](#batchgetrulesresponse)
Read specific rules by id — typically to resolve the `rule_id` a
ResolvedCategory carries into the rule that produced it. Ids the caller
cannot see, or that do not exist, are omitted from the response rather than
causing an error.
#### PreviewRule
PreviewRule ([PreviewRuleRequest](#previewrulerequest)) [PreviewRuleResponse](#previewruleresponse)
Report what a candidate rule would change, without saving it.
The candidate is slotted into the workspace's live rule set — replacing an
existing rule when `id` names one — and the whole resolution is run over
every check, so scope, priority, explicit categories and producer-declared
categories are all honoured. The response separates the candidate's raw
reach from its real effect, which are rarely the same number.
Preview a rule before saving it: one that matches broadly can recategorise
a large part of a workspace in a single sweep.
#### ListRuleMatches
ListRuleMatches ([ListRuleMatchesRequest](#listrulematchesrequest)) [ListRuleMatchesResponse](#listrulematchesresponse)
Report how many checks each rule currently categorises, with a sample of
them — the state already applied, not a projection. Use it to show
"categorises N checks" next to each rule, and to spot a rule that has
stopped matching anything. For the full, paginated list of one rule's
checks, call ChecksCategoriesService.ListCheckCategories with the rule id.
#### GetRecategorisationStatus
GetRecategorisationStatus ([GetRecategorisationStatusRequest](#getrecategorisationstatusrequest)) [GetRecategorisationStatusResponse](#getrecategorisationstatusresponse)
Progress of the re-categorisation sweep a rule change schedules. The sweep
normally completes in seconds, so this is how to confirm that the
workspace's existing checks have caught up with the new rules — not
something a client has to wait on.
### ChecksCategoriesService
(synq.entities.checks.v1.ChecksCategoriesService)
ChecksCategoriesService lets workspace admins set explicit category
overrides on individual checks. An explicit category is the
authoritative category for a check — it takes precedence over the
categories computed by the workspace's categorisation rules.
It is a public API so customers can manage check categories
programmatically; the same service is also mounted on the internal
API. The workspace and the acting identity are always taken from the
request context, never from the payload.
#### UpsertCheckCategories
UpsertCheckCategories ([UpsertCheckCategoriesRequest](#upsertcheckcategoriesrequest)) [UpsertCheckCategoriesResponse](#upsertcheckcategoriesresponse)
UpsertCheckCategories sets (or replaces) the explicit category for
one or more checks. Each entry is the complete state for its check —
both dimensions — so an empty `category` / `governance_category` means
"no explicit value for that dimension".
#### DeleteCheckCategories
DeleteCheckCategories ([DeleteCheckCategoriesRequest](#deletecheckcategoriesrequest)) [DeleteCheckCategoriesResponse](#deletecheckcategoriesresponse)
DeleteCheckCategories clears the explicit category (both dimensions)
for the given checks. Checks fall back to their rule-computed
categories afterwards.
#### ListCheckCategories
ListCheckCategories ([ListCheckCategoriesRequest](#listcheckcategoriesrequest)) [ListCheckCategoriesResponse](#listcheckcategoriesresponse)
ListCheckCategories returns every explicit check category in the
caller's workspace.
### ChecksCoverageService
(synq.entities.checks.v1.ChecksCoverageService)
ChecksCoverageService provides functionality to retrieve and analyze the coverage of checks
for entities in the system. It helps track which checks are applied to entities, including
both direct checks and propagated checks from upstream entities.
#### GetCoverage
GetCoverage ([GetCoverageRequest](#getcoveragerequest)) [GetCoverageResponse](#getcoverageresponse)
GetCoverage retrieves the coverage of checks for a specific entity.
This includes both direct checks applied to the entity and checks propagated
from upstream entities. The response provides detailed information about:
* Table-level checks
* Column-level checks
* Check propagation paths
* Column usage statistics
### PlatformUsersService
(synq.entities.platformusers.v1alpha.PlatformUsersService)
PlatformUsersService reads the logins of a workspace's data platforms, as its integrations declare
them and as their queries show them: how each login reads, why, how much it does and what it costs.
Logins, their emails and what they read are often personal data, so every read here needs the
platform users scope, which a Developer and an Admin have.
#### ListPlatformUsers
ListPlatformUsers ([ListPlatformUsersRequest](#listplatformusersrequest)) [ListPlatformUsersResponse](#listplatformusersresponse)
List the workspace's logins by their share of the workspace's activity, highest first. The first
page carries a summary of the whole window.
#### BatchGetPlatformUsers
BatchGetPlatformUsers ([BatchGetPlatformUsersRequest](#batchgetplatformusersrequest)) [BatchGetPlatformUsersResponse](#batchgetplatformusersresponse)
Read logins by platform user id. Ids no integration declared and with no activity in the window
are left out.
#### ListLinkSuggestions
ListLinkSuggestions ([ListLinkSuggestionsRequest](#listlinksuggestionsrequest)) [ListLinkSuggestionsResponse](#listlinksuggestionsresponse)
Suggest which workspace member each unclassified login may belong to, by matching the login
against the members the caller passes. Accept a suggestion with synq.users.v1
UsersService.UpsertUserAliases. A login already linked is skipped only when the caller lists its
platform\_user\_id among a member's identities.
### ServiceAccountsService
(synq.entities.platformusers.v1alpha.ServiceAccountsService)
ServiceAccountsService maintains a workspace's service accounts, person links and popularity
policies: which logins act for a tool, which belong to a person, and whose reads count toward an
entity's popularity.
Every change goes through a plan first. A typical loop is: ListPlatformUsers with unclassified\_only
to see the logins nothing classifies, ListServiceAccountSuggestions for the accounts their evidence
points at, PlanServiceAccountChanges to see what a change set moves, then
ApplyServiceAccountChanges with the plan's token. A change takes effect on the next read.
Exact matchers, suggestion evidence and plans name logins, which are often personal data. A caller
without the platform users scope gets them with every platform user id, login and person identity
cleared, and the counts kept. A suggestion's account name and login patterns come from the logins it
saw, so that caller gets those cleared too, and a suggestion named after its one login is named
"Service login".
#### ListServiceAccounts
ListServiceAccounts ([ListServiceAccountsRequest](#listserviceaccountsrequest)) [ListServiceAccountsResponse](#listserviceaccountsresponse)
List every workspace service account, then every built-in one, in the order they apply, and the
person links in a second list.
#### BatchGetServiceAccounts
BatchGetServiceAccounts ([BatchGetServiceAccountsRequest](#batchgetserviceaccountsrequest)) [BatchGetServiceAccountsResponse](#batchgetserviceaccountsresponse)
Read service accounts by id, built-in ids included. Ids that do not exist are left out.
#### ListServiceAccountSuggestions
ListServiceAccountSuggestions ([ListServiceAccountSuggestionsRequest](#listserviceaccountsuggestionsrequest)) [ListServiceAccountSuggestionsResponse](#listserviceaccountsuggestionsresponse)
Suggest service accounts for the logins the evidence says are not a person, each as a change set
with its evidence. A login a workspace account, a person link or Coalesce Quality takes is left
out. A login only a built-in account holds is suggested too, so it can get an account of its own. A suggestion changes nothing by itself: edit it if needed, plan it with
PlanServiceAccountChanges, then apply it.
#### PlanServiceAccountChanges
PlanServiceAccountChanges ([PlanServiceAccountChangesRequest](#planserviceaccountchangesrequest)) [PlanServiceAccountChangesResponse](#planserviceaccountchangesresponse)
Report what a change set would change, without writing it: every login slice whose
classification moves, how many counted reads each adds or removes, and the entities whose rank
moves most. The plan\_token it returns is what ApplyServiceAccountChanges takes.
#### ApplyServiceAccountChanges
ApplyServiceAccountChanges ([ApplyServiceAccountChangesRequest](#applyserviceaccountchangesrequest)) [ApplyServiceAccountChangesResponse](#applyserviceaccountchangesresponse)
Write a planned change set, all of it or nothing. It is refused with FAILED\_PRECONDITION when the
token is not from a plan of exactly this change set against the workspace as it is now: plan it
again. A change to an account or person link whose etag no longer matches is refused with
ABORTED, and nothing is written. Caller-supplied ids and etags make a retry converge.
### SqlInsightsService
(synq.entities.sql\_insights.v1.SqlInsightsService)
SqlInsightsService exposes analytical information about the SQL used across a
workspace's entities. It answers questions such as "which SQL constructs are
used in my warehouse, and by how many entities", "what SQL constructs does
this specific entity use" and "which columns does building this entity join
on", without requiring the caller to re-parse any SQL.
#### ListSqlConstructs
ListSqlConstructs ([ListSqlConstructsRequest](#listsqlconstructsrequest)) [ListSqlConstructsResponse](#listsqlconstructsresponse)
ListSqlConstructs returns the distribution of SQL constructs across all
entities in the workspace. Every count is a number of entities, not a number
of occurrences: an entity whose SQL contains five GROUP BY clauses adds one
to the HAS\_GROUP\_BY count, not five. The result is workspace-scoped (derived
from the authenticated request) — no entity paths are supplied. Useful for
building filter/autocomplete UIs and for high-level "shape of our SQL"
analytics.
#### BatchSqlDetails
BatchSqlDetails ([BatchSqlDetailsRequest](#batchsqldetailsrequest)) [BatchSqlDetailsResponse](#batchsqldetailsresponse)
BatchSqlDetails returns the SQL details for each of the requested entities,
keyed by their synq path: the set of detected SQL constructs and, when the
request asks for them, the joins that building the entity makes. Every
requested path appears in the response, including a path with no SQL
details and a path matching no entity; both are returned with an empty set
of constructs and no joins.
#### ListJoinPatterns
ListJoinPatterns ([ListJoinPatternsRequest](#listjoinpatternsrequest)) [ListJoinPatternsResponse](#listjoinpatternsresponse)
ListJoinPatterns lists how other SQL joins a table, or one of its columns:
every join key that the SQL of a table, view or model in the workspace
uses to join the requested entity, read from the requested entity's side,
and which SQL makes it. It is the reverse of `BatchSqlDetails` joins, which
say how building one entity joins others: here the requested entity is the
one being joined.
The requested entity is taken together with every entity that is the same
object, like a dbt model, the table it builds and that table in a
catalog, so asking about any of them returns the joins on all of them.
### EntitiesService
(synq.entities.entities.v1.EntitiesService)
EntitiesService is a service for retriving any entity.
#### GetEntity
GetEntity ([GetEntityRequest](#getentityrequest)) [GetEntityResponse](#getentityresponse)
Get specific entity by its identifier.
#### BatchGetEntities
BatchGetEntities ([BatchGetEntitiesRequest](#batchgetentitiesrequest)) [BatchGetEntitiesResponse](#batchgetentitiesresponse)
Get multiple entities by their identifiers.
#### ListEntities
ListEntities ([ListEntitiesRequest](#listentitiesrequest)) [ListEntitiesResponse](#listentitiesresponse)
List entities matching the given filter
#### SearchEntities
SearchEntities ([SearchEntitiesRequest](#searchentitiesrequest)) [SearchEntitiesResponse](#searchentitiesresponse)
Search for entities.
### IdentifierResolveService
(synq.entities.resolve.v1.IdentifierResolveService)
IdentifierResolveService resolves identifiers to their Coalesce Quality paths and identities.
#### BatchResolveIdentifiers
BatchResolveIdentifiers ([BatchResolveIdentifiersRequest](#batchresolveidentifiersrequest)) [BatchResolveIdentifiersResponse](#batchresolveidentifiersresponse)
Resolve multiple identifiers, returning their Coalesce Quality paths and identity groups.
### CodeService
(synq.entities.code.v1.CodeService)
CodeService is a service for retrieving code associated with entities in the system.
It provides functionality to access and manage code artifacts such as SQL queries,
Python scripts, dbt models, and other code configurations that are part of Coalesce Quality entities.
#### ListCode
ListCode ([ListCodeRequest](#listcoderequest)) [ListCodeResponse](#listcoderesponse)
ListCode retrieves all code artifacts associated with a specific entity.
This includes SQL queries, Python scripts, dbt models, and other code configurations
that define or are part of the entity's functionality.
### TypeDefsService
(synq.entities.typedefs.v1.TypeDefsService)
TypeDefsService tells you what the type values in every other endpoint mean.
Responses across the API describe things by enum — an entity is an
`ENTITY_TYPE_BQ_TABLE`, it belongs to `DATA_PLATFORM_TYPE_BIGQUERY`, a run is an
`EXECUTION_TYPE_DBT_INVOCATION`. This service turns each of those into a name you
can display and a set of facts you can act on: is this a transformation model,
does it live in a warehouse, can it be monitored.
Read it instead of keeping your own copy of the list. New entity types and new
platforms are added as Coalesce Quality supports more tools, and a hard-coded
table in your code will label them with whatever its fallback produces and miss
that an existing type gained a capability. Everything here comes from the server,
so you pick both up without changing your code.
A typical integration calls them once at startup, keeps them in a map, and looks
types up as responses arrive.
It also tells you what a selection query can ask for: ListEntityTraitDefs is the
vocabulary behind `traits`, which selects entities by what they are rather than
by naming every type.
Scoping and freshness:
* ListEntityTypeDefs is per workspace, because the types you defined yourself
are part of your workspace's vocabulary. It returns the built-in types and
your own custom types together.
* ListPlatformDefs, ListExecutionTypeDefs and ListEntityTraitDefs are the same
for everyone.
* The built-in half changes only when Coalesce Quality is upgraded, so caching
it for the life of your process is fine. You can add or edit your own custom
types at any time, so if a newly created type needs to appear promptly, cache
the entity types for minutes rather than hours, or re-read after you change
one.
The workspace comes from your credentials; there is nothing to pass.
Each list is small — hundreds of entries — so every call returns the whole set
and none of them page.
#### ListEntityTypeDefs
ListEntityTypeDefs ([ListEntityTypeDefsRequest](#listentitytypedefsrequest)) [ListEntityTypeDefsResponse](#listentitytypedefsresponse)
List every kind of entity in your workspace, the built-in types and your own.
The built-in half covers every value of `synq.entities.v1.EntityType` this
server serves, so this is also how you discover a type that your generated code
is too old to name. Your own types have no enum value of their own — each one
is identified by its `type_id`.
#### ListPlatformDefs
ListPlatformDefs ([ListPlatformDefsRequest](#listplatformdefsrequest)) [ListPlatformDefsResponse](#listplatformdefsresponse)
List every platform Coalesce Quality connects to, with its display name.
Use it to name the platform on an entity — "BigQuery", "Microsoft SQL Server" —
rather than deriving a name from the enum value.
#### ListEntityTraitDefs
ListEntityTraitDefs ([ListEntityTraitDefsRequest](#listentitytraitdefsrequest)) [ListEntityTraitDefsResponse](#listentitytraitdefsresponse)
List every trait a selection query can filter entities by.
Read it before writing a selection that uses `traits`, and to show a person
what one covers. A trait name this list does not contain selects nothing, so
reading the list is how you tell a trait that does not exist from one your
client is too old to know about.
#### ListExecutionTypeDefs
ListExecutionTypeDefs ([ListExecutionTypeDefsRequest](#listexecutiontypedefsrequest)) [ListExecutionTypeDefsResponse](#listexecutiontypedefsresponse)
List every kind of run Coalesce Quality records against an entity.
A dbt invocation, a warehouse query job, a detected schema change. Use it to
name the `execution_type` that execution and run endpoints return.
### EntityExecutionsService
(synq.entities.executions.v2.EntityExecutionsService)
EntityExecutionsService provides read-only access to entity execution history.
This service allows customers to retrieve information about all executions that happened on their entities,
including execution status, timing, and messages.
Use cases:
* Retrieve execution history for specific entities
* Filter executions by time range, status, or execution type
* Get aggregated summaries of execution activity
* Track execution trends and patterns
#### ListExecutions
ListExecutions ([ListExecutionsRequest](#listexecutionsrequest)) [ListExecutionsResponse](#listexecutionsresponse)
ListExecutions retrieves a paginated list of executions for one or more entities.
Executions are returned in reverse chronological order (newest first).
Supports filtering by:
* Entity identifiers (required)
* Time range
* Execution type(s)
* Status(es)
#### BatchGetExecutions
BatchGetExecutions ([BatchGetExecutionsRequest](#batchgetexecutionsrequest)) [BatchGetExecutionsResponse](#batchgetexecutionsresponse)
BatchGetExecutions retrieves multiple executions by their IDs.
This is useful when you already know the execution IDs and want to fetch their details.
#### GetExecutionsSummary
GetExecutionsSummary ([GetExecutionsSummaryRequest](#getexecutionssummaryrequest)) [GetExecutionsSummaryResponse](#getexecutionssummaryresponse)
GetExecutionsSummary provides aggregated statistics about executions for specified entities.
This is useful for quickly understanding execution patterns without fetching all execution details.
Returns:
* Counts by execution type
* Counts by status
* Time range of available executions
* Latest execution per entity
#### GetLatestExecutions
GetLatestExecutions ([GetLatestExecutionsRequest](#getlatestexecutionsrequest)) [GetLatestExecutionsResponse](#getlatestexecutionsresponse)
GetLatestExecutions retrieves the most recent execution for each specified entity.
IMPORTANT: Only returns executions where the entity appears in EntityExecution.entities (what executed).
This means it returns executions OF the entity (e.g., model runs, monitor runs, task runs),
NOT executions that merely reference the entity in targets or extra\_references.
For example, querying for a table won't return results because tables don't execute on their own -
they are created/updated by models (which do execute).
Optionally filter by execution status to get the latest execution with a specific status.
### EntityExecutionsService
(synq.entities.executions.v1.EntityExecutionsService)
Deprecated: Use \[synq.entities.custom.v1.EntityExecutionsService] instead
#### UpsertExecution
UpsertExecution ([UpsertExecutionRequest](#upsertexecutionrequest)) [UpsertExecutionResponse](#upsertexecutionresponse)
Execution affects status of the custom entity
#### UpsertLogEntry
UpsertLogEntry ([UpsertLogEntryRequest](#upsertlogentryrequest)) [UpsertLogEntryResponse](#upsertlogentryresponse)
LogEntry is a log message for the custom entity
### LineageFoldsService
(synq.entities.lineage.folds.v1alpha.LineageFoldsService)
LineageFoldsService manages the lineage folds of a workspace: rules that show
many similar entities, a table or a storage path written anew on every run,
as one node in lineage. See `LineageFold`.
A change to a fold reaches lineage shortly after the call returns, not
within it.
Fold ids are UUIDs chosen by the caller, so a retried create converges on
the same fold instead of making a second one. Pass the `etag` you last read
on a change to guard against overwriting a concurrent edit.
This API is in alpha: its shape may change without notice.
#### CreateFold
CreateFold ([CreateFoldRequest](#createfoldrequest)) [CreateFoldResponse](#createfoldresponse)
Create a fold. Repeating the same request returns the fold it created;
creating a different fold under an id already in use is refused with
ALREADY\_EXISTS.
#### ListFolds
ListFolds ([ListFoldsRequest](#listfoldsrequest)) [ListFoldsResponse](#listfoldsresponse)
List the ids of every fold in the workspace, switched off ones included,
oldest first. Read the folds with `BatchGetFolds`.
#### BatchGetFolds
BatchGetFolds ([BatchGetFoldsRequest](#batchgetfoldsrequest)) [BatchGetFoldsResponse](#batchgetfoldsresponse)
Read folds by id. An id with no fold is absent from the response.
#### BatchGetFoldNodes
BatchGetFoldNodes ([BatchGetFoldNodesRequest](#batchgetfoldnodesrequest)) [BatchGetFoldNodesResponse](#batchgetfoldnodesresponse)
Read fold nodes by entity id: which fold a node in a lineage response
stands for, and which entities it showed. An id is answered for as long as
lineage may still return it; an id with nothing to answer is absent from
the response.
#### RenameFold
RenameFold ([RenameFoldRequest](#renamefoldrequest)) [RenameFoldResponse](#renamefoldresponse)
Change the name shown on a fold's node.
#### UpdateFoldMatcher
UpdateFoldMatcher ([UpdateFoldMatcherRequest](#updatefoldmatcherrequest)) [UpdateFoldMatcherResponse](#updatefoldmatcherresponse)
Change which entities a fold matches. The fold's node keeps its entity id.
#### SetFoldEnabled
SetFoldEnabled ([SetFoldEnabledRequest](#setfoldenabledrequest)) [SetFoldEnabledResponse](#setfoldenabledresponse)
Switch a fold on or off. A fold switched off is kept and listed, and
lineage shows the entities it matched as their own nodes again.
#### DeleteFold
DeleteFold ([DeleteFoldRequest](#deletefoldrequest)) [DeleteFoldResponse](#deletefoldresponse)
Delete a fold. Deleting a fold that does not exist succeeds.
#### PreviewFold
PreviewFold ([PreviewFoldRequest](#previewfoldrequest)) [PreviewFoldResponse](#previewfoldresponse)
Show what a matcher would fold, before it is saved: how many entities and
lineage edges it matches, a sample of them, and the entities that stop
being shown on the same node as a folded one. Answered from the lineage the
workspace has now, without any fold applied.
### LineageService
(synq.entities.lineage.v1.LineageService)
LineageService allows you to fetch:
* Entity level lineage from a starting point of one or more entities.
* Column Level lineage from a starting point of multiple columns of a single entity.
#### GetLineage
GetLineage ([GetLineageRequest](#getlineagerequest)) [GetLineageResponse](#getlineageresponse)
### UsersService
(synq.users.v1.UsersService)
#### ListUsers
ListUsers ([ListUsersRequest](#listusersrequest)) [ListUsersResponse](#listusersresponse)
#### BatchGetUsers
BatchGetUsers ([BatchGetUsersRequest](#batchgetusersrequest)) [BatchGetUsersResponse](#batchgetusersresponse)
#### GetByAlias
GetByAlias ([GetByAliasRequest](#getbyaliasrequest)) [GetByAliasResponse](#getbyaliasresponse)
#### GetByEmail
GetByEmail ([GetByEmailRequest](#getbyemailrequest)) [GetByEmailResponse](#getbyemailresponse)
#### UpsertUserAliases
UpsertUserAliases ([UpsertUserAliasesRequest](#upsertuseraliasesrequest)) [UpsertUserAliasesResponse](#upsertuseraliasesresponse)
Links aliases to users.
A platform user alias needs SCOPE\_PLATFORM\_USER\_LINK, every other alias type
SCOPE\_USER\_EDIT. A request holding an alias the caller may not write is
refused as a whole, and nothing is written.
#### DeleteUserAliases
DeleteUserAliases ([DeleteUserAliasesRequest](#deleteuseraliasesrequest)) [DeleteUserAliasesResponse](#deleteuseraliasesresponse)
Removes aliases from users.
A platform user alias removes only that login, every other alias type removes
the user's alias of that type. The scopes are checked per alias, as for
UpsertUserAliases.
#### AddUserRole
AddUserRole ([AddUserRoleRequest](#adduserrolerequest)) [AddUserRoleResponse](#adduserroleresponse)
Grants a role to a user in the workspace.
Granting a role the user already holds succeeds without change. Granting a
role to a user who is not a member of the workspace makes them a member;
such a user can only be named by their synq or email identity.
#### RemoveUserRole
RemoveUserRole ([RemoveUserRoleRequest](#removeuserrolerequest)) [RemoveUserRoleResponse](#removeuserroleresponse)
Withdraws a role from a user in the workspace.
Withdrawing a role the user does not hold succeeds without change.
Withdrawing the user's last role removes them from the workspace, together
with their linked aliases.
#### GetUserAliases
GetUserAliases ([GetUserAliasesRequest](#getuseraliasesrequest)) [GetUserAliasesResponse](#getuseraliasesresponse)
#### GetUserIdentities
GetUserIdentities ([GetUserIdentitiesRequest](#getuseridentitiesrequest)) [GetUserIdentitiesResponse](#getuseridentitiesresponse)
Returns all canonical identity strings for a user.
If user\_id is omitted, returns identities for the currently authenticated user.
These identity strings can be passed to ListIssues(interacted\_by\_identities)
to filter issues the user has interacted with.
Format: "synq:\", "email:\", "slack:\", "msteams:\",
and the "platform\_user:" id of every linked platform user.
### SavedViewsService
(synq.savedviews.v1.SavedViewsService)
SavedViewsService manages saved views — named, reusable selections over your
entities (and other surfaces) with display configuration, sharing and
per-user pinning.
A saved view is owned by the user who created it. It can be kept private,
shared with the whole workspace, or granted to specific users. Reads return,
for the calling user, how each view relates to them (mine / shared / granted)
and what they are allowed to do with it. Workspace administrators can manage
any view in the workspace.
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Create or update a saved view.
The view id is supplied by the caller (a UUID), which makes this operation
idempotent — repeating the same request converges to the same view rather
than creating a duplicate. Mutable fields are optional: a field that is set
is written, a field that is omitted is left unchanged. To guard against
overwriting a concurrent edit, pass the `etag` you last read.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete a saved view. Idempotent — deleting an already-deleted or unknown
view succeeds. Pass `etag` to guard against deleting a concurrently-edited
view.
#### BatchGet
BatchGet ([BatchGetRequest](#batchgetrequest)) [BatchGetResponse](#batchgetresponse)
Fetch multiple saved views by id. Ids the caller cannot see, or that do not
exist, are omitted from the response rather than causing an error.
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
List the saved views the calling user can see. By default returns every
visible view (their own, workspace-owned, shared-with-workspace and
granted-to-them); narrow with `scopes`, `context` and `only_pinned`.
#### SetPin
SetPin ([SetPinRequest](#setpinrequest)) [SetPinResponse](#setpinresponse)
Pin or unpin a saved view to the calling user's main screen. Pinning is a
per-user preference: it applies only to the caller and does not modify the
view or its version, so any user who can see a view may pin it.
### IamService
(synq.auth.iam.v1.IamService)
#### Iam
Iam ([IamRequest](#iamrequest)) [IamResponse](#iamresponse)
Iam returns information about caller authentication
### KnowledgeService
(synq.knowledge.v1alpha.KnowledgeService)
KnowledgeService reads the knowledge a team keeps about its data: knowledge
pages written or imported by the team, and the descriptions people write for
entities.
Knowledge pages form a tree, and pages and entities are pinned to each
other. Nothing here is the description an entity's own source reports, which
`synq.entities.entities.v1.EntitiesService` reads.
This API is in alpha: its shape may change without notice.
#### BatchGetEntityKnowledge
BatchGetEntityKnowledge ([BatchGetEntityKnowledgeRequest](#batchgetentityknowledgerequest)) [BatchGetEntityKnowledgeResponse](#batchgetentityknowledgeresponse)
Read the knowledge held about entities: the description people wrote for
each one, and the knowledge pages pinned to it.
An entity with no knowledge is absent from the response, as is an id that
names no entity. Returns UNAVAILABLE when the knowledge could not be read,
which is worth retrying.
#### ListKnowledgePages
ListKnowledgePages ([ListKnowledgePagesRequest](#listknowledgepagesrequest)) [ListKnowledgePagesResponse](#listknowledgepagesresponse)
List the ids of knowledge pages, ordered by name, optionally narrowed by a
part of their name and by certification. Deprecated pages are left out
unless asked for. Read the pages with `BatchGetKnowledgePages`.
#### BatchGetKnowledgePages
BatchGetKnowledgePages ([BatchGetKnowledgePagesRequest](#batchgetknowledgepagesrequest)) [BatchGetKnowledgePagesResponse](#batchgetknowledgepagesresponse)
Read knowledge pages by id: their content, where they sit in the tree,
who owns them and how they are tagged. A page that does not exist, or was
deleted, is absent from the response. Set `skip_content` to read
everything but the content, for showing a listing.
#### ListKnowledgePageLinks
ListKnowledgePageLinks ([ListKnowledgePageLinksRequest](#listknowledgepagelinksrequest)) [ListKnowledgePageLinksResponse](#listknowledgepagelinksresponse)
List what is pinned to a knowledge page, in either direction: the ids of
the entities and of the other knowledge pages.
### DataproductsService
(synq.dataproducts.v2.DataproductsService)
DataproductsService manages data products — named, owned groupings of data
assets with a membership definition, a priority and an optional folder.
Ids are opaque UUIDs supplied by the caller, which makes writes idempotent
(a retried create converges instead of duplicating). Mutable fields are
optional on Upsert: a field that is set is written, a field that is omitted is
left unchanged. Pass the `etag` you last read to guard against overwriting a
concurrent edit.
A data product is a leaf in the reference graph — its definition may not
reference another data product or domain — so membership resolution always
terminates.
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Create or update a data product. The id is supplied by the caller (a UUID);
repeating the same request converges to the same product. Mutable fields are
optional: a set field is written, an omitted field is left unchanged. Pass
`etag` to guard against overwriting a concurrent edit.
#### BatchGet
BatchGet ([BatchGetRequest](#batchgetrequest)) [BatchGetResponse](#batchgetresponse)
Fetch multiple data products by id. Ids that do not exist (or, unless
`include_deleted` is set, are deleted) are omitted from the response rather
than causing an error.
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
List the data products in the workspace, paginated. Narrow with `folder`
and `priorities`.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete a data product. Idempotent — deleting an already-deleted or unknown
product succeeds. Soft-deletes by default; set `purge` to hard-delete and
release the id. Pass `etag` to guard against deleting a concurrently-edited
product.
#### SetDefinition
SetDefinition ([SetDefinitionRequest](#setdefinitionrequest)) [SetDefinitionResponse](#setdefinitionresponse)
Replace the entire membership definition of a data product.
#### UpsertDefinitionPart
UpsertDefinitionPart ([UpsertDefinitionPartRequest](#upsertdefinitionpartrequest)) [UpsertDefinitionPartResponse](#upsertdefinitionpartresponse)
Add or replace a single definition part (matched by its part id).
#### RemoveDefinitionPart
RemoveDefinitionPart ([RemoveDefinitionPartRequest](#removedefinitionpartrequest)) [RemoveDefinitionPartResponse](#removedefinitionpartresponse)
Remove a single definition part by its part id. Idempotent.
#### ListMembers
ListMembers ([ListMembersRequest](#listmembersrequest)) [ListMembersResponse](#listmembersresponse)
List the assets that currently belong to a data product, resolved from its
definition and returned as opaque entity ids. Paginated.
### DataproductsService
(synq.dataproducts.v1.DataproductsService)
DataproductsService can be used to manage data products.
#### Upsert
Upsert ([UpsertRequest](#upsertrequest)) [UpsertResponse](#upsertresponse)
Create or update a dataproduct.
#### Get
Get ([GetRequest](#getrequest)) [GetResponse](#getresponse)
Get multiple dataproducts by their identifiers.
#### List
List ([ListRequest](#listrequest)) [ListResponse](#listresponse)
List all the dataproducts in the current scope.
#### Delete
Delete ([DeleteRequest](#deleterequest)) [DeleteResponse](#deleteresponse)
Delete a dataproduct by its identifier.
Soft deletes by default. Optionally purge the dataproduct to release the identifier.
#### SetDefinition
SetDefinition ([SetDefinitionRequest](#setdefinitionrequest)) [SetDefinitionResponse](#setdefinitionresponse)
Set the definition for an existing dataproduct.
#### UpsertDefinitionPart
UpsertDefinitionPart ([UpsertDefinitionPartRequest](#upsertdefinitionpartrequest)) [UpsertDefinitionPartResponse](#upsertdefinitionpartresponse)
Upsert definition part to the definition of an existing dataproduct.
#### RemoveDefinitionPart
RemoveDefinitionPart ([RemoveDefinitionPartRequest](#removedefinitionpartrequest)) [RemoveDefinitionPartResponse](#removedefinitionpartresponse)
Remove definition part from the definition of an existing dataproduct.
### OwnersService
(synq.owners.v1.OwnersService)
OwnersService manages owners and their ownerships — the "alert routing as
code" surface. An owner is a named responsible party with notification
channels (contacts); an ownership assigns a set of assets to an owner and
configures the alerts routed to it. Owner is the resource, ownership its
sub-resource: an ownership is meaningless without its owner, and deleting an
owner deletes its ownerships.
Ids are opaque UUIDs supplied by the caller, which makes writes idempotent (a
retried create converges instead of duplicating). Mutable fields are optional
on the Upsert calls: a set field is written, an omitted field is left
unchanged. Pass the `etag` you last read to guard against overwriting a
concurrent edit.
Related: `synq.dataproducts.v2.DataproductsService` (an ownership can own a
whole data product), `synq.alerts.services.v2.AlertsService` (how the configured alerts
are reported), and `synq.users.v1.UsersService` (resolve the users behind
`UserContact` and Slack/Teams mentions).
#### UpsertOwner
UpsertOwner ([UpsertOwnerRequest](#upsertownerrequest)) [UpsertOwnerResponse](#upsertownerresponse)
Create or update an owner. The id is supplied by the caller (a UUID);
repeating the same request converges to the same owner. Mutable fields are
optional: a set field is written, an omitted field is left unchanged. Pass
`etag` to guard against overwriting a concurrent edit.
#### BatchGetOwners
BatchGetOwners ([BatchGetOwnersRequest](#batchgetownersrequest)) [BatchGetOwnersResponse](#batchgetownersresponse)
Fetch multiple owners by id. Ids that do not exist (or, unless
`include_deleted` is set, are deleted) are omitted from the response rather
than causing an error.
#### ListOwners
ListOwners ([ListOwnersRequest](#listownersrequest)) [ListOwnersResponse](#listownersresponse)
List the owners in the workspace, paginated.
#### DeleteOwner
DeleteOwner ([DeleteOwnerRequest](#deleteownerrequest)) [DeleteOwnerResponse](#deleteownerresponse)
Delete an owner and all of its ownerships. Idempotent — deleting an
already-deleted or unknown owner succeeds. Soft-deletes by default; set
`purge` to hard-delete and release the id. Pass `etag` to guard against
deleting a concurrently-edited owner.
#### UpsertOwnership
UpsertOwnership ([UpsertOwnershipRequest](#upsertownershiprequest)) [UpsertOwnershipResponse](#upsertownershipresponse)
Create or update an ownership under an owner. The ownership id is supplied
by the caller (a UUID). Assigning a data product that is already owned by a
different ownership moves it (the previous ownership of that product is
removed), matching the app.
#### ListOwnerships
ListOwnerships ([ListOwnershipsRequest](#listownershipsrequest)) [ListOwnershipsResponse](#listownershipsresponse)
List the ownerships of an owner, paginated.
#### BatchGetOwnerships
BatchGetOwnerships ([BatchGetOwnershipsRequest](#batchgetownershipsrequest)) [BatchGetOwnershipsResponse](#batchgetownershipsresponse)
Fetch multiple ownerships by id (across owners). Ids that do not exist (or,
unless `include_deleted` is set, are deleted) are omitted.
#### DeleteOwnership
DeleteOwnership ([DeleteOwnershipRequest](#deleteownershiprequest)) [DeleteOwnershipResponse](#deleteownershipresponse)
Delete an ownership by id. Idempotent — deleting an already-deleted or
unknown ownership succeeds. Pass `etag` to guard against deleting a
concurrently-edited ownership.
### CloudwatchService
(synq.ingest.cloudwatch.v1.CloudwatchService)
#### IngestCloudwatchLogsData
IngestCloudwatchLogsData ([IngestCloudwatchLogsDataRequest](#ingestcloudwatchlogsdatarequest)) [IngestCloudwatchLogsDataResponse](#ingestcloudwatchlogsdataresponse)
### SqlMeshService
(synq.ingest.sqlmesh.v1.SqlMeshService)
#### IngestMetadata
IngestMetadata ([IngestMetadataRequest](#ingestmetadatarequest)) [IngestMetadataResponse](#ingestmetadataresponse)
#### IngestExecution
IngestExecution ([IngestExecutionRequest](#ingestexecutionrequest)) [IngestExecutionResponse](#ingestexecutionresponse)
### DwhService
(synq.ingest.dwh.v1.DwhService)
#### IngestObjectInformation
IngestObjectInformation ([IngestObjectInformationRequest](#ingestobjectinformationrequest)) [IngestObjectInformationResponse](#ingestobjectinformationresponse)
#### IngestSqlDefinitions
IngestSqlDefinitions ([IngestSqlDefinitionsRequest](#ingestsqldefinitionsrequest)) [IngestSqlDefinitionsResponse](#ingestsqldefinitionsresponse)
#### IngestSchemas
IngestSchemas ([IngestSchemasRequest](#ingestschemasrequest)) [IngestSchemasResponse](#ingestschemasresponse)
#### IngestObjectMetrics
IngestObjectMetrics ([IngestObjectMetricsRequest](#ingestobjectmetricsrequest)) [IngestObjectMetricsResponse](#ingestobjectmetricsresponse)
#### IngestTaskResults
IngestTaskResults ([IngestTaskResultsRequest](#ingesttaskresultsrequest)) [IngestTaskResultsResponse](#ingesttaskresultsresponse)
#### IngestQueryLogs
IngestQueryLogs ([IngestQueryLogsRequest](#ingestquerylogsrequest)) [IngestQueryLogsResponse](#ingestquerylogsresponse)
#### IngestTableConstraints
IngestTableConstraints ([IngestTableConstraintsRequest](#ingesttableconstraintsrequest)) [IngestTableConstraintsResponse](#ingesttableconstraintsresponse)
#### IngestLineage
IngestLineage ([IngestLineageRequest](#ingestlineagerequest)) [IngestLineageResponse](#ingestlineageresponse)
IngestLineage pushes data-flow lineage between warehouse objects (and,
optionally, between their columns) into the platform. Send the lineage you
extract from the warehouse — for example from Snowflake's GET\_LINEAGE — as a
batch of directed edges. Use `replace_mode` to choose between an
incremental update (MERGE) and an authoritative refresh of a region of the
warehouse (REPLACE\_SCOPE); REPLACE\_SCOPE is what lets the platform expire
edges that no longer exist. Safe to retry: re-sending the same batch
converges to the same state.
### AirflowLogsService
(synq.ingest.airflow\.v1.AirflowLogsService)
#### IngestLog
IngestLog ([IngestLogRequest](#ingestlogrequest)) [IngestLogResponse](#ingestlogresponse)
#### IngestDatahubAspects
IngestDatahubAspects ([IngestDatahubAspectsRequest](#ingestdatahubaspectsrequest)) [IngestDatahubAspectsResponse](#ingestdatahubaspectsresponse)
Accepts what a DataHub REST emitter sends when its server is set to
`/api/ingest/airflow/v1`: it posts to `{server}/aspects?action=ingestProposal`.
### DbtService
(synq.ingest.dbt.v1.DbtService)
#### IngestInvocation
IngestInvocation ([IngestInvocationRequest](#ingestinvocationrequest)) [IngestInvocationResponse](#ingestinvocationresponse)
### OpenlineageService
(synq.ingest.openlineage.v1.OpenlineageService)
#### Ingest
Ingest ([IngestRequest](#ingestrequest)) [IngestResponse](#ingestresponse)
### TriggerService
(synq.datachecks.v1.TriggerService)
TriggerService provides synchronous execution of datachecks on entities.
#### TriggerDatachecks
TriggerDatachecks ([TriggerDatachecksRequest](#triggerdatachecksrequest)) [TriggerDatachecksResponse](#triggerdatachecksresponse)
TriggerDatachecks executes datachecks for the specified entities.
### SqlTestsService
(synq.datachecks.sqltests.v1.SqlTestsService)
SqlTestsService is a service for managing SqlTests.
#### BatchUpsertSqlTests
BatchUpsertSqlTests ([BatchUpsertSqlTestsRequest](#batchupsertsqltestsrequest)) [BatchUpsertSqlTestsResponse](#batchupsertsqltestsresponse)
Upsert SqlTests based on a unique ID.
#### ListSqlTests
ListSqlTests ([ListSqlTestsRequest](#listsqltestsrequest)) [ListSqlTestsResponse](#listsqltestsresponse)
List SqlTests for given annotations.
#### BatchGetSqlTests
BatchGetSqlTests ([BatchGetSqlTestsRequest](#batchgetsqltestsrequest)) [BatchGetSqlTestsResponse](#batchgetsqltestsresponse)
Get SqlTests by their IDs.
#### BatchDeleteSqlTests
BatchDeleteSqlTests ([BatchDeleteSqlTestsRequest](#batchdeletesqltestsrequest)) [BatchDeleteSqlTestsResponse](#batchdeletesqltestsresponse)
Delete SqlTests by their IDs.
#### GetSqlTestAudit
GetSqlTestAudit ([GetSqlTestAuditRequest](#getsqltestauditrequest)) [GetSqlTestAuditResponse](#getsqltestauditresponse)
Get SqlTest audit from a given execution ID in jwt token.
### SqlTestsDeploymentRulesService
(synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService)
SqlTestsDeploymentRulesService manages SqlTestDeploymentRules — rules that
dynamically deploy SQL test templates onto every table or view a query
matches, resyncing over time as the match set changes.
#### GetSqlTestDeployOverview
GetSqlTestDeployOverview ([GetSqlTestDeployOverviewRequest](#getsqltestdeployoverviewrequest)) [GetSqlTestDeployOverviewResponse](#getsqltestdeployoverviewresponse)
Preview the effect of deploying a rule (which have not yet been saved), without
deploying it.
#### GetSqlTestDeployDeleteOverview
GetSqlTestDeployDeleteOverview ([GetSqlTestDeployDeleteOverviewRequest](#getsqltestdeploydeleteoverviewrequest)) [GetSqlTestDeployDeleteOverviewResponse](#getsqltestdeploydeleteoverviewresponse)
Preview the effect of deleting a rule, without deleting it.
#### ListSqlTestDeploymentRules
ListSqlTestDeploymentRules ([ListSqlTestDeploymentRulesRequest](#listsqltestdeploymentrulesrequest)) [ListSqlTestDeploymentRulesResponse](#listsqltestdeploymentrulesresponse)
#### BatchGetSqlTestDeploymentRules
BatchGetSqlTestDeploymentRules ([BatchGetSqlTestDeploymentRulesRequest](#batchgetsqltestdeploymentrulesrequest)) [BatchGetSqlTestDeploymentRulesResponse](#batchgetsqltestdeploymentrulesresponse)
#### DeploySqlTestDeploymentRule
DeploySqlTestDeploymentRule ([DeploySqlTestDeploymentRuleRequest](#deploysqltestdeploymentrulerequest)) [DeploySqlTestDeploymentRuleResponse](#deploysqltestdeploymentruleresponse)
Deploy (create or update) a SqlTestDeploymentRule and immediately sync tests
onto its current match set.
#### BatchDeploySqlTestDeploymentRules
BatchDeploySqlTestDeploymentRules ([BatchDeploySqlTestDeploymentRulesRequest](#batchdeploysqltestdeploymentrulesrequest)) [BatchDeploySqlTestDeploymentRulesResponse](#batchdeploysqltestdeploymentrulesresponse)
#### DeleteSqlTestDeploymentRule
DeleteSqlTestDeploymentRule ([DeleteSqlTestDeploymentRuleRequest](#deletesqltestdeploymentrulerequest)) [DeleteSqlTestDeploymentRuleResponse](#deletesqltestdeploymentruleresponse)
#### BatchDeleteSqlTestDeploymentRules
BatchDeleteSqlTestDeploymentRules ([BatchDeleteSqlTestDeploymentRulesRequest](#batchdeletesqltestdeploymentrulesrequest)) [BatchDeleteSqlTestDeploymentRulesResponse](#batchdeletesqltestdeploymentrulesresponse)
### TestSuggestionsService
(synq.datachecks.testsuggestions.v1.TestSuggestionsService)
#### BatchCreateTestSuggestions
BatchCreateTestSuggestions ([BatchCreateTestSuggestionsRequest](#batchcreatetestsuggestionsrequest)) [BatchCreateTestSuggestionsResponse](#batchcreatetestsuggestionsresponse)
#### BatchGetTestSuggestionsForEntities
BatchGetTestSuggestionsForEntities ([BatchGetTestSuggestionsForEntitiesRequest](#batchgettestsuggestionsforentitiesrequest)) [BatchGetTestSuggestionsForEntitiesResponse](#batchgettestsuggestionsforentitiesresponse)
#### BatchAcceptTestSuggestions
BatchAcceptTestSuggestions ([BatchAcceptTestSuggestionsRequest](#batchaccepttestsuggestionsrequest)) [BatchAcceptTestSuggestionsResponse](#batchaccepttestsuggestionsresponse)
#### BatchRejectTestSuggestions
BatchRejectTestSuggestions ([BatchRejectTestSuggestionsRequest](#batchrejecttestsuggestionsrequest)) [BatchRejectTestSuggestionsResponse](#batchrejecttestsuggestionsresponse)
## Messages
### Issue
(synq.issues.issues.v1.Issue)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | ID of the issue. |
| name | [ string](#string) | Name of the issue. |
| message | [ string](#string) | Original message of the issue. |
| current\_message | [ string](#string) | Current message on the issue. |
| last\_error\_message | [ string](#string) | Last error message on the issue. |
| severity | [ Severity](#severity) | Severity of the issue. |
| trigger\_entity | [ Identifier](#identifier) | Entity that triggered this issue. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) trigger\_details.monitor | [ MonitorTriggerDetails](#monitortriggerdetails) | none |
| affected\_entities | [repeated Identifier](#identifier) | Entities affected by this issue. |
| state | [ State](#state) | State of the issue. |
| started\_at | [ Timestamp](#timestamp) | Started at timestamp. |
| updated\_at | [ Timestamp](#timestamp) | Updated at timestamp. |
| ended\_at | [ Timestamp](#timestamp) | Ended at timestamp. |
| latest\_status | [ Status](#status) | Latest status of the issue. |
| latest\_status\_actor | [ Actor](#actor) | Actor that last updated the status of the issue. |
| comments | [repeated IssueComment](#issuecomment) | List of comments posted on the issue. |
| lifecycle\_state | [ LifecycleState](#lifecyclestate) | Lifecycle state of the issue. |
### IssueComment
(synq.issues.issues.v1.IssueComment)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| message | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| actor | [ Actor](#actor) | none |
### MonitorTriggerDetails
(synq.issues.issues.v1.MonitorTriggerDetails)
| Field | Type | Description |
| - | - | - |
| monitor | [ MonitorIdentifier](#monitoridentifier) | none |
| metric\_id | [ string](#string) | none |
| metric\_version | [ int32](#int32) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_segment\_value.segment\_value | [optional string](#string) | none |
### BatchGetIssuesRequest
(synq.issues.issues.v1.BatchGetIssuesRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | IDs of the issues to get. |
### BatchGetIssuesResponse
(synq.issues.issues.v1.BatchGetIssuesResponse)
| Field | Type | Description |
| - | - | - |
| issues | [map IssuesEntry](#issuesentry) | Issues. |
### BatchGetIssuesResponse.IssuesEntry
(synq.issues.issues.v1.BatchGetIssuesResponse.IssuesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Issue](#issue) | none |
### Limit
(synq.issues.issues.v1.Limit)
| Field | Type | Description |
| - | - | - |
| max | [ int32](#int32) | none |
| after\_id | [ string](#string) | none |
### ListIssuesRequest
(synq.issues.issues.v1.ListIssuesRequest)
| Field | Type | Description |
| - | - | - |
| from | [ Timestamp](#timestamp) | Filter by time range. |
| to | [ Timestamp](#timestamp) | Filter by time range. |
| limit | [ Limit](#limit) | none |
| entities | [repeated Identifier](#identifier) | Filter by entities. |
### ListIssuesResponse
(synq.issues.issues.v1.ListIssuesResponse)
| Field | Type | Description |
| - | - | - |
| issues | [repeated Issue](#issue) | Issues. |
### MarkExpectedRequest
(synq.issues.issues.v1.MarkExpectedRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as expected. |
| actor | [ Actor](#actor) | Actor marking the issue as expected. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as expected. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkExpectedResponse
(synq.issues.issues.v1.MarkExpectedResponse)
### MarkFixedRequest
(synq.issues.issues.v1.MarkFixedRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as fixed. |
| actor | [ Actor](#actor) | Actor marking the issue as fixed. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as fixed. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkFixedResponse
(synq.issues.issues.v1.MarkFixedResponse)
### MarkInvestigatingRequest
(synq.issues.issues.v1.MarkInvestigatingRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as investigating. |
| actor | [ Actor](#actor) | Actor marking the issue as investigating. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as investigating. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkInvestigatingResponse
(synq.issues.issues.v1.MarkInvestigatingResponse)
### MarkNoActionNeededRequest
(synq.issues.issues.v1.MarkNoActionNeededRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as no action needed. |
| actor | [ Actor](#actor) | Actor marking the issue as no action needed. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as no action needed. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkNoActionNeededResponse
(synq.issues.issues.v1.MarkNoActionNeededResponse)
### PostCommentRequest
(synq.issues.issues.v1.PostCommentRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to post a comment on. |
| actor | [ Actor](#actor) | Actor posting the comment. |
| comment | [ string](#string) | Comment to post. |
| time | [ Timestamp](#timestamp) | Time at which the comment was posted. Defaults to the current time. |
### PostCommentResponse
(synq.issues.issues.v1.PostCommentResponse)
| Field | Type | Description |
| - | - | - |
| comment\_id | [ string](#string) | none |
### BatchGetIssuesRequest
(synq.issues.v2.BatchGetIssuesRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | IDs of the issues to get. |
### BatchGetIssuesResponse
(synq.issues.v2.BatchGetIssuesResponse)
| Field | Type | Description |
| - | - | - |
| issues | [map IssuesEntry](#issuesentry) | Issues. |
### BatchGetIssuesResponse.IssuesEntry
(synq.issues.v2.BatchGetIssuesResponse.IssuesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Issue](#issue) | none |
### ClearStatusRequest
(synq.issues.v2.ClearStatusRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue whose status should be cleared. |
| actor | [ Actor](#actor) | Actor clearing the status. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_time.time | [optional Timestamp](#timestamp) | Time at which the status was cleared. Defaults to the current time when not provided. |
### ClearStatusResponse
(synq.issues.v2.ClearStatusResponse)
### DeleteCommentRequest
(synq.issues.v2.DeleteCommentRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue the comment is on. |
| comment\_id | [ string](#string) | ID of the comment to delete, as returned by PostComment or read from the issue's comments. |
| actor | [ Actor](#actor) | Actor deleting the comment. Must match the actor that posted it. |
### DeleteCommentResponse
(synq.issues.v2.DeleteCommentResponse)
### InteractionFilter
(synq.issues.v2.InteractionFilter)
Filter for matching issues where an actor interacted with them.
If none of status\_changes/comments/activities is set, all interaction types are matched.
| Field | Type | Description |
| - | - | - |
| identities | [repeated string](#string) | Identity strings to match against the actor that performed the interaction. The caller is responsible for resolving a user to all their known identities (e.g. via GetUserIdentities or GetUserAliases + actoridentities.FromUserAliases()) before calling this API. Format: "synq:\", "email:\", "slack:\", etc. |
| status\_changes | [ bool](#bool) | Match issues where the user set a status (investigating, fixed, expected, etc.). |
| comments | [ bool](#bool) | Match issues where the user posted a comment. |
| activities | [ bool](#bool) | Match issues where the user has an activity record. |
### ListIssuesRequest
(synq.issues.v2.ListIssuesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_entities\_query.entities\_query | [optional Query](#query) | Filter by entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_time.time | [optional Time](#time) | Time range filter |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sort.sort | [optional Sort](#sort) | Sort options |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_lifecycle\_state.lifecycle\_state | [optional LifecycleState](#lifecyclestate) | Filter by issue lifecycle state |
| lifecycle\_states | [repeated LifecycleState](#lifecyclestate) | Filter by issue lifecycle states |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_interacted\_by.interacted\_by | [optional InteractionFilter](#interactionfilter) | Filter issues by users who interacted with them (status change, comment, or activity). |
| severities | [repeated Severity](#severity) | Filter by issue severity. If empty, issues of any severity are returned. |
### ListIssuesRequest.Sort
(synq.issues.v2.ListIssuesRequest.Sort)
| Field | Type | Description |
| - | - | - |
| sort\_by | [ Field](#field) | none |
| order | [ Order](#order) | none |
### ListIssuesRequest.Time
(synq.issues.v2.ListIssuesRequest.Time)
| Field | Type | Description |
| - | - | - |
| field | [ Field](#field) | none |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
### ListIssuesResponse
(synq.issues.v2.ListIssuesResponse)
| Field | Type | Description |
| - | - | - |
| page\_info | [ PageInfo](#pageinfo) | Paging information. |
| issue\_ids | [repeated string](#string) | none |
### MarkExpectedRequest
(synq.issues.v2.MarkExpectedRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as expected. |
| actor | [ Actor](#actor) | Actor marking the issue as expected. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as expected. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkExpectedResponse
(synq.issues.v2.MarkExpectedResponse)
### MarkFixedRequest
(synq.issues.v2.MarkFixedRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as fixed. |
| actor | [ Actor](#actor) | Actor marking the issue as fixed. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as fixed. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkFixedResponse
(synq.issues.v2.MarkFixedResponse)
### MarkInvestigatingRequest
(synq.issues.v2.MarkInvestigatingRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as investigating. |
| actor | [ Actor](#actor) | Actor marking the issue as investigating. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as investigating. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkInvestigatingResponse
(synq.issues.v2.MarkInvestigatingResponse)
### MarkNoActionNeededRequest
(synq.issues.v2.MarkNoActionNeededRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to mark as no action needed. |
| actor | [ Actor](#actor) | Actor marking the issue as no action needed. |
| time | [ Timestamp](#timestamp) | Time at which the issue was marked as no action needed. Defaults to the current time. |
| require\_no\_existing\_status | [ bool](#bool) | Ignore status change if the issue already has a status. |
### MarkNoActionNeededResponse
(synq.issues.v2.MarkNoActionNeededResponse)
### PostCommentRequest
(synq.issues.v2.PostCommentRequest)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | ID of the issue to post a comment on. |
| actor | [ Actor](#actor) | Actor posting the comment. |
| comment | [ string](#string) | Comment to post. |
| time | [ Timestamp](#timestamp) | Time at which the comment was posted. Defaults to the current time. |
### PostCommentResponse
(synq.issues.v2.PostCommentResponse)
| Field | Type | Description |
| - | - | - |
| comment\_id | [ string](#string) | none |
### Actor
(synq.issues.actor.v1.Actor)
Actor identifies who performed a write — set by the calling client and
carried end-to-end through the public API into stored audit trails (issue
status changes, comments, incident assignments) and rendered downstream
(e.g. Slack/MSTeams/email alerts).
Producers should populate:
1. `name` — human-readable display label, derived from
`synq.auth.iam.v1.IamResponse.user_name` when available, falling back
to `user_email`. Avoid generic placeholders ("MCP", "API"); readers
treat those as "no identity resolved" and fall back to impersonal copy.
2. `user` — the strongest identifier the caller can prove. For human
callers, set `email` from `IamResponse.user_email` so the server can
resolve the caller back to a workspace user.
3. `via` — entry-point label for the channel through which the request
arrived (e.g. VIA\_MCP for the MCP server), regardless of who the
caller is.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) user.slack | [ SlackUser](#slackuser) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) user.email | [ EmailUser](#emailuser) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) user.pagerduty | [ PagerdutyUser](#pagerdutyuser) | none |
| via | [ Via](#via) | none |
### EmailUser
(synq.issues.actor.v1.EmailUser)
| Field | Type | Description |
| - | - | - |
| user\_email | [ string](#string) | none |
### PagerdutyUser
(synq.issues.actor.v1.PagerdutyUser)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
### SlackUser
(synq.issues.actor.v1.SlackUser)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
### IssuesCommand
(synq.issues.commands.v1.IssuesCommand)
Not to be used directly. Use the IssuesService instead when calling via API.
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.mark\_investigating | [ MarkInvestigatingRequest](#markinvestigatingrequest) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.mark\_fixed | [ MarkFixedRequest](#markfixedrequest) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.mark\_expected | [ MarkExpectedRequest](#markexpectedrequest) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.mark\_no\_action\_needed | [ MarkNoActionNeededRequest](#marknoactionneededrequest) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.post\_comment | [ PostCommentRequest](#postcommentrequest) | none |
### CommitDiff
(synq.git.commits.v1.CommitDiff)
| Field | Type | Description |
| - | - | - |
| commit\_sha | [ string](#string) | none |
| file\_diffs | [repeated FileDiff](#filediff) | none |
### FileDiff
(synq.git.commits.v1.FileDiff)
| Field | Type | Description |
| - | - | - |
| file\_path | [ string](#string) | none |
| diff | [ string](#string) | none |
### GetDiffByChangeIdRequest
(synq.git.commits.v1.GetDiffByChangeIdRequest)
| Field | Type | Description |
| - | - | - |
| change\_id | [ string](#string) | none |
| limit\_to\_files | [repeated string](#string) | none |
### GetDiffByChangeIdResponse
(synq.git.commits.v1.GetDiffByChangeIdResponse)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_commit\_diff.commit\_diff | [optional CommitDiff](#commitdiff) | none |
### GetDiffByCommitRequest
(synq.git.commits.v1.GetDiffByCommitRequest)
| Field | Type | Description |
| - | - | - |
| commit\_sha | [ string](#string) | Commit SHA. Accepts either the full 40-char hex hash or any unique hex prefix of at least 7 characters (server resolves the prefix against stored commits, like `git rev-parse `). Ambiguous or unmatched prefixes return an empty diff rather than an error. |
| limit\_to\_files | [repeated string](#string) | none |
### GetDiffByCommitResponse
(synq.git.commits.v1.GetDiffByCommitResponse)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_commit\_diff.commit\_diff | [optional CommitDiff](#commitdiff) | none |
### EditIntegrationAction
(synq.recommendations.v1.EditIntegrationAction)
EditIntegrationAction opens an integration's settings, where the change is
made through `synq.integrations.v1.IntegrationsService.UpdateIntegration` or
the app.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration (UUID). |
| config\_fields | [repeated string](#string) | The configuration fields to change, named as in the integration's configuration message (`job_ids` on a dbt Cloud integration), so a client can take you straight to them. Empty when the change is not about particular fields. |
### IntegrationTarget
(synq.recommendations.v1.IntegrationTarget)
IntegrationTarget is one integration of the workspace.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration (UUID), as `synq.integrations.v1.IntegrationsService` knows it. |
### LinkAction
(synq.recommendations.v1.LinkAction)
LinkAction is a page that explains how to make the change: Coalesce Quality
documentation, or the documentation of a system the workspace connects to.
| Field | Type | Description |
| - | - | - |
| title | [ string](#string) | What the link is about ("Selecting dbt Cloud jobs", "Snowflake: GRANT privileges"), to show as the link text. Always set. |
| url | [ string](#string) | Absolute https URL of the page. |
| platform | [ DataPlatformType](#dataplatformtype) | Which system the page belongs to, to show that platform's icon beside the link. Unspecified for Coalesce Quality documentation or where no platform fits. |
### NewIntegrationAction
(synq.recommendations.v1.NewIntegrationAction)
NewIntegrationAction starts connecting a new integration of a platform the
workspace has none of, where it is made through
`synq.integrations.v1.IntegrationsService.CreateIntegration` or the app.
| Field | Type | Description |
| - | - | - |
| platform | [ DataPlatformType](#dataplatformtype) | The platform to connect. A platform with several kinds of integration has one action per kind (`DATA_PLATFORM_TYPE_DBT_CLOUD` and `DATA_PLATFORM_TYPE_DBT_SELF_HOSTED` for dbt). A client that cannot connect the platform offers no button for it. |
### OpenInAppAction
(synq.recommendations.v1.OpenInAppAction)
OpenInAppAction opens a place in the Coalesce Quality app. The place is
named rather than given as a URL, so a client builds the link for its own
routes.
| Field | Type | Description |
| - | - | - |
| destination | [ AppDestination](#appdestination) | none |
### Recommendation
(synq.recommendations.v1.Recommendation)
Recommendation is a change Coalesce Quality recommends you make: a grant an
integration's role is missing, a setting that stopped matching what runs, a
feature worth enabling, a review waiting for you. Nothing is broken; making
the change gets you more, or better, out of the product.
A recommendation stays for as long as the condition holds, and disappears on
its own once it no longer does. You can dismiss it until something new is
found, or ignore it for good.
| Field | Type | Description |
| - | - | - |
| recommendation\_id | [ string](#string) | Stable id of the recommendation (UUID). The same recommendation for the same target always has the same id, also after it disappeared and came back, so a dismissal or an ignore keeps applying to it. |
| target | [ RecommendationTarget](#recommendationtarget) | What the recommendation is about. |
| code | [ string](#string) | Stable identifier of the kind of recommendation (`stale_job_selection`), lower snake\_case. It never changes for a given kind, so it is safe to filter or group on; the wording of `message` and `remediation` may improve. |
| subject | [ string](#string) | What within the target the recommendation is about, when one target can carry the same kind of recommendation about several things ("job selection", a database name). Empty when it concerns the target as a whole. |
| message | [ string](#string) | What was found, as one sentence. |
| remediation | [ string](#string) | What to change, and where. |
| details | [repeated string](#string) | The objects the recommendation concerns, one per entry. |
| details\_omitted | [ int32](#int32) | How many further objects exist beyond `details`. Zero when `details` lists all of them. |
| actions | [repeated RecommendationAction](#recommendationaction) | What you can do about it, most useful first, for a client to offer as buttons beside `remediation`. Empty when `remediation` is all there is to go on. |
| first\_stated\_at | [ Timestamp](#timestamp) | When the condition was first found, without it clearing in between. |
| changed\_at | [ Timestamp](#timestamp) | When what is reported last changed: a new object, a different remediation, a reworded message, different actions. |
| state | [ RecommendationState](#recommendationstate) | Whether the recommendation is in front of you, after any dismissal or ignore. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_decision.decision | [optional RecommendationDecision](#recommendationdecision) | The dismissal or ignore standing against the recommendation. Still present when it reopened because something new was found since it was dismissed. Absent when nobody decided. |
### RecommendationAction
(synq.recommendations.v1.RecommendationAction)
RecommendationAction is one thing you can do about a recommendation.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.edit\_integration | [ EditIntegrationAction](#editintegrationaction) | Open an integration's settings. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.link | [ LinkAction](#linkaction) | Open a page that explains the change. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.open\_in\_app | [ OpenInAppAction](#openinappaction) | Open a place in the Coalesce Quality app. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.new\_integration | [ NewIntegrationAction](#newintegrationaction) | Start connecting a new integration. |
### RecommendationDecision
(synq.recommendations.v1.RecommendationDecision)
RecommendationDecision is a dismissal or an ignore.
| Field | Type | Description |
| - | - | - |
| kind | [ RecommendationDecisionKind](#recommendationdecisionkind) | none |
| decided\_at | [ Timestamp](#timestamp) | When the decision was made. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_decided\_by.decided\_by | [optional Actor](#actor) | Who made it. Absent when the request identified no one. |
### RecommendationTarget
(synq.recommendations.v1.RecommendationTarget)
RecommendationTarget is what a recommendation is about.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.workspace | [ WorkspaceTarget](#workspacetarget) | The workspace as a whole: a feature to enable, a review waiting. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.integration | [ IntegrationTarget](#integrationtarget) | One integration: a grant its role lacks, a setting to change. |
### WorkspaceTarget
(synq.recommendations.v1.WorkspaceTarget)
WorkspaceTarget is the workspace as a whole.
### BatchGetRecommendationsRequest
(synq.recommendations.v1.BatchGetRecommendationsRequest)
Request to fetch recommendations by id.
| Field | Type | Description |
| - | - | - |
| recommendation\_ids | [repeated string](#string) | Ids of the recommendations (UUIDs). |
### BatchGetRecommendationsResponse
(synq.recommendations.v1.BatchGetRecommendationsResponse)
Response with the recommendations found, keyed by id.
| Field | Type | Description |
| - | - | - |
| recommendations | [map RecommendationsEntry](#recommendationsentry) | none |
### BatchGetRecommendationsResponse.RecommendationsEntry
(synq.recommendations.v1.BatchGetRecommendationsResponse.RecommendationsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Recommendation](#recommendation) | none |
### DismissRecommendationRequest
(synq.recommendations.v1.DismissRecommendationRequest)
Request to dismiss a recommendation.
| Field | Type | Description |
| - | - | - |
| recommendation\_id | [ string](#string) | Id of the recommendation (UUID). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Who is deciding. Optional: identity is completed from the calling credentials. |
### DismissRecommendationResponse
(synq.recommendations.v1.DismissRecommendationResponse)
Response with the recommendation as stored after the call.
| Field | Type | Description |
| - | - | - |
| recommendation | [ Recommendation](#recommendation) | none |
### IgnoreRecommendationRequest
(synq.recommendations.v1.IgnoreRecommendationRequest)
Request to ignore a recommendation.
| Field | Type | Description |
| - | - | - |
| recommendation\_id | [ string](#string) | Id of the recommendation (UUID). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Who is deciding. Optional: identity is completed from the calling credentials. |
### IgnoreRecommendationResponse
(synq.recommendations.v1.IgnoreRecommendationResponse)
Response with the recommendation as stored after the call.
| Field | Type | Description |
| - | - | - |
| recommendation | [ Recommendation](#recommendation) | none |
### ListRecommendationsRequest
(synq.recommendations.v1.ListRecommendationsRequest)
Request to list recommendation ids.
| Field | Type | Description |
| - | - | - |
| target\_kinds | [repeated RecommendationTargetKind](#recommendationtargetkind) | Limit the list to these kinds of target. Empty lists every kind. |
| integration\_ids | [repeated string](#string) | Limit the list to recommendations about these integrations (UUIDs). Implies the integration target kind. Empty applies no integration filter. |
| states | [repeated RecommendationState](#recommendationstate) | Limit the list to recommendations in these states. Empty lists every state; pass `RECOMMENDATION_STATE_OPEN` for the ones still in front of you. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Cursor-based pagination. Omit to get every id in one response; set it without a `page_size` for pages of the default size. |
### ListRecommendationsResponse
(synq.recommendations.v1.ListRecommendationsResponse)
Response with a page of recommendation ids.
| Field | Type | Description |
| - | - | - |
| recommendation\_ids | [repeated string](#string) | none |
| page\_info | [ PageInfo](#pageinfo) | Pass `page_info.last_id` as the next cursor. Empty once there are no more. |
### RestoreRecommendationRequest
(synq.recommendations.v1.RestoreRecommendationRequest)
Request to restore a recommendation.
| Field | Type | Description |
| - | - | - |
| recommendation\_id | [ string](#string) | Id of the recommendation (UUID). |
### RestoreRecommendationResponse
(synq.recommendations.v1.RestoreRecommendationResponse)
Response with the recommendation as stored after the call.
| Field | Type | Description |
| - | - | - |
| recommendation | [ Recommendation](#recommendation) | none |
### ModeAnomalyEngine
(synq.monitors.custom\_monitors.v1.ModeAnomalyEngine)
| Field | Type | Description |
| - | - | - |
| sensitivity | [ Sensitivity](#sensitivity) | none |
### ModeFixedThresholds
(synq.monitors.custom\_monitors.v1.ModeFixedThresholds)
| Field | Type | Description |
| - | - | - |
| min | [ DoubleValue](#doublevalue) | none |
| max | [ DoubleValue](#doublevalue) | none |
### MonitorCategoryDistribution
(synq.monitors.custom\_monitors.v1.MonitorCategoryDistribution)
| Field | Type | Description |
| - | - | - |
| field | [ string](#string) | Categorical column whose value distribution is monitored. |
| top\_k\_limit | [ int32](#int32) | Max categories to track; the rest are aggregated into "other". 0 = use default. |
### MonitorCustomNumeric
(synq.monitors.custom\_monitors.v1.MonitorCustomNumeric)
| Field | Type | Description |
| - | - | - |
| metric\_aggregation | [ string](#string) | none |
| never\_negative | [ bool](#bool) | The aggregation is never negative, so the anomaly engine never forecasts below 0. |
### MonitorDefinition
(synq.monitors.custom\_monitors.v1.MonitorDefinition)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| monitored\_id | [ Identifier](#identifier) | none |
| time\_partitioning | [ TimePartitioning](#timepartitioning) | How the monitored data is split into time segments. When omitted, the monitor takes one whole-data snapshot per scheduled run instead of segmenting by time (and gets no historical backfill on its first run). Must be omitted for table\_stats monitors. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_segmentation.segmentation | [optional Segmentation](#segmentation) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_filter.filter | [optional string](#string) | none |
| severity | [ Severity](#severity) | none |
| name | [ string](#string) | none |
| description | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID this monitor was authored from, when deployed via configuration as code. Unset for monitors authored directly through this API. |
On a write this field is only ever set, never unset: omitting it keeps the stored value, so a full-replace update cannot detach the monitor from the config that owns it. An empty string is a real config id — the one a deploy that declares no namespace uses — so sending it attaches the monitor to that config rather than clearing the field. No value detaches a monitor; recreating it is the only way to hand it back to the app. |
\| source | [ Source](#source) | Source of the monitor. |
\| timezone | [ string](#string) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) monitor.freshness | [ MonitorFreshness](#monitorfreshness) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) monitor.volume | [ MonitorVolume](#monitorvolume) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) monitor.custom\_numeric | [ MonitorCustomNumeric](#monitorcustomnumeric) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) monitor.field\_stats | [ MonitorFieldStats](#monitorfieldstats) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) monitor.table\_stats | [ MonitorTableStats](#monitortablestats) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) monitor.category\_distribution | [ MonitorCategoryDistribution](#monitorcategorydistribution) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mode.anomaly\_engine | [ ModeAnomalyEngine](#modeanomalyengine) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mode.fixed\_thresholds | [ ModeFixedThresholds](#modefixedthresholds) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) schedule.daily | [ ScheduleDaily](#scheduledaily) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) schedule.hourly | [ ScheduleHourly](#schedulehourly) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) schedule.on\_demand | [ ScheduleOnDemand](#scheduleondemand) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sql\_source.sql\_source | [optional SqlSource](#sqlsource) | Optional SQL source. When set, the monitor queries against this SQL (wrapped as a CTE) instead of the table identified by monitored\_id directly. Column references in time\_partitioning, segmentation, and filter refer to columns in the SQL output. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Technical category this monitor declares for itself — what kind of check it is mechanically (e.g. "volume", "freshness"). It is the monitor's own declaration, not an administrative override: it is authoritative over any categorisation rule the workspace would otherwise apply, and is itself overridden by an explicit per-check category an admin sets.
The value is free-form and workspace-defined — whatever vocabulary your categorisation rules already use. It is displayed with underscores turned into spaces, so `snake_case` reads well.
Send the whole monitor on every upsert: leaving this out is the same as sending it empty, and both mean the monitor declares no technical category. A monitor then falls back to its `quality.check_category` annotation, which remains accepted and keeps working — this field simply outranks it.
A segmented monitor's segments take this value too. A segment is the same monitor sliced by a column value, so it cannot declare a category of its own. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | Governance category this monitor declares for itself — what the check is for, the data quality dimension governance reports on (e.g. "timeliness"). Resolves independently of `category` — setting one never suppresses the other — and follows the same precedence, the same free-form vocabulary and the same full-state upsert rule described above. Its annotation fallback is `quality.governance_category`. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | When the monitor was deleted. Unset while it is live, which is every monitor an ordinary listing reports; a deleted one comes back only from a listing that asked for it. Read-only, and ignored on writes. |
### MonitorFieldStats
(synq.monitors.custom\_monitors.v1.MonitorFieldStats)
| Field | Type | Description |
| - | - | - |
| fields | [repeated string](#string) | Columns monitored with every metric of their data type. A column may appear here or in field\_options, not both. At least one of fields / field\_options must be non-empty. |
| field\_options | [repeated FieldOption](#fieldoption) | Columns monitored with an explicit metric selection. The sensitivity of each metric applies to that metric instead of the monitor's sensitivity. Changing the selection keeps the history of the metrics that remain and does not reset the monitor: a removed metric stops being predicted, and adding it back resumes it. |
### MonitorFieldStats.FieldOption
(synq.monitors.custom\_monitors.v1.MonitorFieldStats.FieldOption)
| Field | Type | Description |
| - | - | - |
| field | [ string](#string) | Column name, as in the table schema. |
| metrics | [repeated MetricOption](#metricoption) | Metrics to predict on this column, each with its sensitivity. Must be metrics that exist for the column's data type (e.g. mean is not available on a text column): - text: NUM\_ROWS, NUM\_NULL, PCT\_NULL, NUM\_UNIQUE, PCT\_UNIQUE, NUM\_EMPTY, PCT\_EMPTY. - numeric: the text metrics plus MEAN, MIN, MAX, MEDIAN, STDDEV. - date and time: NUM\_ROWS, NUM\_NULL, PCT\_NULL, NUM\_UNIQUE, PCT\_UNIQUE, MIN, MAX. A metric the column's data warehouse cannot compute is skipped (Redshift does not compute NUM\_UNIQUE or PCT\_UNIQUE on numeric columns), so a column whose whole selection is skipped produces no predictions. |
### MonitorFreshness
(synq.monitors.custom\_monitors.v1.MonitorFreshness)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | The expression to filter on (eg. "created\_at", "meta\['created\_at']") |
### MonitorTableStats
(synq.monitors.custom\_monitors.v1.MonitorTableStats)
| Field | Type | Description |
| - | - | - |
| metric\_options | [repeated MetricOption](#metricoption) | Metrics to monitor. If empty, all metrics are monitored. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) freshness\_source.field | [ string](#string) | Timestamp column name, e.g. "updated\_at" (used as max(updated\_at)). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) freshness\_source.sql | [ string](#string) | Row-level SQL expression yielding a timestamp, e.g. "coalesce(updated\_at, created\_at)". Do NOT wrap it in an aggregate — max() is applied automatically. |
| delay\_model | [ DelayModel](#delaymodel) | How the delay and volume\_change\_delay metrics model the expected arrival time of new data. Unset leaves the stored value unchanged. |
### MonitorVolume
(synq.monitors.custom\_monitors.v1.MonitorVolume)
### ScheduleDaily
(synq.monitors.custom\_monitors.v1.ScheduleDaily)
| Field | Type | Description |
| - | - | - |
| minutes\_since\_midnight | [ int32](#int32) | Minutes since midnight (0-1439) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_delay\_num\_days.delay\_num\_days | [optional int32](#int32) | Delay monitoring by given number of days. |
| only\_schedule\_delay | [ bool](#bool) | if true, the minutes\_since\_midnight will be related to delay scheduling/execution instead of time shifting time partitions |
### ScheduleHourly
(synq.monitors.custom\_monitors.v1.ScheduleHourly)
| Field | Type | Description |
| - | - | - |
| minute\_of\_hour | [ int32](#int32) | Minute of hour (0-59) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_delay\_num\_hours.delay\_num\_hours | [optional int32](#int32) | Delay monitoring by given number of hours. |
| only\_schedule\_delay | [ bool](#bool) | if true, the minute\_of\_hour will be related to delay scheduling/execution instead of time shifting time partitions |
### ScheduleOnDemand
(synq.monitors.custom\_monitors.v1.ScheduleOnDemand)
### Segmentation
(synq.monitors.custom\_monitors.v1.Segmentation)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | The expression to segment on (eg. "country", "meta\['country']") You can optionally specify values to include or exclude from segmentation. If neither are provided, all values are included by default. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_include\_values.include\_values | [optional ValueList](#valuelist) | List of expression values to include for segmentation (eg. "UK", "France") |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_exclude\_values.exclude\_values | [optional ValueList](#valuelist) | List of expression values to exclude for segmentation (eg. "NA", "") |
### SqlSource
(synq.monitors.custom\_monitors.v1.SqlSource)
SQL query to use as the data source for a monitor instead of querying a table directly.
The SQL is wrapped as a CTE, and the monitor's metric query runs against its output.
Column references (time partitioning, segmentation, filter) refer to columns in the SQL output.
| Field | Type | Description |
| - | - | - |
| sql | [ string](#string) | The SQL query providing the data source. Example: "SELECT created\_at, country, amount FROM orders WHERE status = 'completed'" |
### TimePartitioning
(synq.monitors.custom\_monitors.v1.TimePartitioning)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | The expression to partition on (eg. "created\_at", "meta\['created\_at']"). Required whenever time\_partitioning is set. To monitor without time segmentation, omit the whole time\_partitioning message rather than sending an empty expression. |
| interval | [ Duration](#duration) | Length of one time segment. Optional: defaults to the interval implied by the schedule (24h for daily, 1h for hourly) and must equal it when set. On-demand monitors have no schedule interval to inherit, so they must set it explicitly whenever time\_partitioning is set. |
### ValueList
(synq.monitors.custom\_monitors.v1.ValueList)
| Field | Type | Description |
| - | - | - |
| values | [repeated string](#string) | none |
### BatchCreateMonitorRequest
(synq.monitors.custom\_monitors.v1.BatchCreateMonitorRequest)
| Field | Type | Description |
| - | - | - |
| monitors | [repeated MonitorDefinition](#monitordefinition) | none |
### BatchCreateMonitorResponse
(synq.monitors.custom\_monitors.v1.BatchCreateMonitorResponse)
| Field | Type | Description |
| - | - | - |
| created\_monitor\_ids | [repeated string](#string) | none |
| errors | [repeated CreateError](#createerror) | One entry per requested monitor that was not created, most commonly because a monitor with that id already exists. Empty when every requested monitor was created. |
### BatchCreateMonitorResponse.CreateError
(synq.monitors.custom\_monitors.v1.BatchCreateMonitorResponse.CreateError)
Details of a requested monitor that was not created.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchDeleteMonitorRequest
(synq.monitors.custom\_monitors.v1.BatchDeleteMonitorRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### BatchDeleteMonitorResponse
(synq.monitors.custom\_monitors.v1.BatchDeleteMonitorResponse)
| Field | Type | Description |
| - | - | - |
| deleted\_ids | [repeated string](#string) | none |
| errors | [repeated DeleteError](#deleteerror) | One entry per requested monitor that was not deleted, most commonly because no monitor with that id exists. Empty when every requested monitor was deleted. |
### BatchDeleteMonitorResponse.DeleteError
(synq.monitors.custom\_monitors.v1.BatchDeleteMonitorResponse.DeleteError)
Details of a requested monitor that was not deleted.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchUpdateMonitorModeRequest
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorModeRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mode.anomaly\_engine | [ ModeAnomalyEngine](#modeanomalyengine) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mode.fixed\_thresholds | [ ModeFixedThresholds](#modefixedthresholds) | none |
### BatchUpdateMonitorModeResponse
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorModeResponse)
| Field | Type | Description |
| - | - | - |
| updated\_ids | [repeated string](#string) | none |
| errors | [repeated UpdateError](#updateerror) | One entry per requested monitor that was not updated, most commonly because no monitor with that id exists. Empty when every requested monitor was updated. |
### BatchUpdateMonitorModeResponse.UpdateError
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorModeResponse.UpdateError)
Details of a requested monitor whose mode was not updated.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchUpdateMonitorRequest
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorRequest)
| Field | Type | Description |
| - | - | - |
| monitor\_ids\_to\_reset | [repeated string](#string) | none |
| monitors | [repeated MonitorDefinition](#monitordefinition) | none |
### BatchUpdateMonitorResponse
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorResponse)
| Field | Type | Description |
| - | - | - |
| updated\_ids | [repeated string](#string) | none |
| errors | [repeated UpdateError](#updateerror) | One entry per requested monitor that was not updated, most commonly because no monitor with that id exists. Empty when every requested monitor was updated. |
### BatchUpdateMonitorResponse.UpdateError
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorResponse.UpdateError)
Details of a requested monitor that was not updated.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchUpdateMonitorSeverityRequest
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorSeverityRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
| severity | [ Severity](#severity) | none |
### BatchUpdateMonitorSeverityResponse
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorSeverityResponse)
| Field | Type | Description |
| - | - | - |
| updated\_ids | [repeated string](#string) | none |
| errors | [repeated UpdateError](#updateerror) | One entry per requested monitor that was not updated, most commonly because no monitor with that id exists. Empty when every requested monitor was updated. |
### BatchUpdateMonitorSeverityResponse.UpdateError
(synq.monitors.custom\_monitors.v1.BatchUpdateMonitorSeverityResponse.UpdateError)
Details of a requested monitor whose severity was not updated.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### ChangeOverview
(synq.monitors.custom\_monitors.v1.ChangeOverview)
| Field | Type | Description |
| - | - | - |
| monitor\_id | [ string](#string) | none |
| origin\_definition | [ MonitorDefinition](#monitordefinition) | none |
| new\_definition | [ MonitorDefinition](#monitordefinition) | none |
| changes | [ string](#string) | none |
| changes\_delta\_json | [ string](#string) | none |
| should\_reset | [ bool](#bool) | none |
### ConfigChangesOverviewRequest
(synq.monitors.custom\_monitors.v1.ConfigChangesOverviewRequest)
| Field | Type | Description |
| - | - | - |
| config\_ids | [repeated string](#string) | none |
| monitors | [repeated MonitorDefinition](#monitordefinition) | none |
### ConfigChangesOverviewResponse
(synq.monitors.custom\_monitors.v1.ConfigChangesOverviewResponse)
| Field | Type | Description |
| - | - | - |
| monitors\_managed\_by\_app | [repeated MonitorDefinition](#monitordefinition) | none |
| monitors\_managed\_by\_other\_configs | [repeated MonitorDefinition](#monitordefinition) | none |
| monitors\_to\_create | [repeated MonitorDefinition](#monitordefinition) | none |
| monitors\_to\_delete | [repeated MonitorDefinition](#monitordefinition) | none |
| monitors\_unchanged | [repeated MonitorDefinition](#monitordefinition) | none |
| monitors\_changes\_overview | [repeated ChangeOverview](#changeoverview) | none |
### ConfigsFilter
(synq.monitors.custom\_monitors.v1.ConfigsFilter)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### ListConfigsMonitorsRequest
(synq.monitors.custom\_monitors.v1.ListConfigsMonitorsRequest)
| Field | Type | Description |
| - | - | - |
| config\_ids | [repeated string](#string) | none |
### ListConfigsMonitorsResponse
(synq.monitors.custom\_monitors.v1.ListConfigsMonitorsResponse)
| Field | Type | Description |
| - | - | - |
| monitors | [repeated MonitorDefinition](#monitordefinition) | none |
### ListMonitorsRequest
(synq.monitors.custom\_monitors.v1.ListMonitorsRequest)
| Field | Type | Description |
| - | - | - |
| integration\_ids | [repeated string](#string) | none |
| monitored\_asset\_paths | [repeated string](#string) | none |
| monitor\_ids | [repeated string](#string) | none |
| source | [repeated string](#string) | none |
| config\_ids | [repeated string](#string) | none |
| configs\_filter | [ ConfigsFilter](#configsfilter) | Filter SqlTests by config IDs. Pass `ConfigsFilter{ids: ["config1", "config2"]}` to filter by specific configs. If nil, no config\_id filtering is applied. If empty, the one with NULL config\_id will be excluded. |
| sources | [repeated Source](#source) | none |
| include\_deleted | [ bool](#bool) | Include deleted monitors in the listing. Off by default, so an ordinary listing keeps describing only what is live. |
Turn it on to resolve a `monitor_ids` entry that may name a deleted monitor. The id this endpoint returns is the only reference a caller holds after creating or listing a monitor, and the monitored asset needed to address the same monitor as an entity is on the stored row and nowhere else, so a listing that excluded it left the id unresolvable.
One entry per monitor either way: a monitor keeps a row per version, and the listing reports the latest one. `deleted_at` on each returned monitor is what tells a deleted monitor from a live one. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination |
### ListMonitorsResponse
(synq.monitors.custom\_monitors.v1.ListMonitorsResponse)
| Field | Type | Description |
| - | - | - |
| monitors | [repeated MonitorDefinition](#monitordefinition) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information. |
### ColumnSource
(synq.monitors.info.v1.ColumnSource)
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | none |
### Condition
(synq.monitors.info.v1.Condition)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | none |
### CountRows
(synq.monitors.info.v1.CountRows)
### DatabaseFqn
(synq.monitors.info.v1.DatabaseFqn)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_instance.instance | [optional string](#string) | Usually it is the hostname of the server |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database\_name.database\_name | [optional string](#string) | Database name for databases with multi-db support |
| schema\_name | [ string](#string) | One level |
| object\_name | [ string](#string) | Name of the table or view |
### ExpressionSource
(synq.monitors.info.v1.ExpressionSource)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | none |
### MetadataSource
(synq.monitors.info.v1.MetadataSource)
### Metric
(synq.monitors.info.v1.Metric)
| Field | Type | Description |
| - | - | - |
| metric\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) metric\_type.freshness | [ MonitorFreshness](#monitorfreshness) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) metric\_type.volume | [ MonitorVolume](#monitorvolume) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) metric\_type.value | [ MonitorValue](#monitorvalue) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) metric\_type.failed\_rows | [ MonitorFailedRows](#monitorfailedrows) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) metric\_type.field\_stats | [ MonitorFieldStats](#monitorfieldstats) | A statistical profile of a single column (for example null counts, uniqueness, minimum/maximum, mean/standard deviation or byte size). The specific statistic is identified by metric\_id; this variant carries the column the statistic is computed on. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) metric\_type.category\_distribution | [ MonitorCategoryDistribution](#monitorcategorydistribution) | A shift in the distribution of the distinct values of a categorical column, scored against a learned baseline. This variant carries the column whose value distribution is monitored. |
### MonitorCategoryDistribution
(synq.monitors.info.v1.MonitorCategoryDistribution)
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Name of the categorical column whose value distribution is monitored. |
### MonitorFailedRows
(synq.monitors.info.v1.MonitorFailedRows)
| Field | Type | Description |
| - | - | - |
| sql | [ string](#string) | none |
### MonitorFieldStats
(synq.monitors.info.v1.MonitorFieldStats)
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Name of the column whose statistical profile is monitored, as it appears in the monitored object. Empty when the statistic is computed at the table level rather than on a specific column. |
### MonitorFreshness
(synq.monitors.info.v1.MonitorFreshness)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) freshness\_source.expression | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) freshness\_source.table\_metadata | [ MetadataSource](#metadatasource) | none |
### MonitorInfo
(synq.monitors.info.v1.MonitorInfo)
| Field | Type | Description |
| - | - | - |
| id | [ MonitorIdentifier](#monitoridentifier) | Identifier of the monitor |
| monitor\_id | [ Identifier](#identifier) | Identifier of the monitor |
| monitor\_synq\_path | [ string](#string) | Coalesce Quality Path to the entity. |
| name | [ string](#string) | Name of the monitor |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_segmentation.segmentation | [optional Segmentation](#segmentation) | Information how this monitor segments the data |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_partitioning.partitioning | [optional Partitioning](#partitioning) | Information how this monitor partitions the data |
| conditions | [repeated Condition](#condition) | Optional conditions which are applied to the monitor metrics retrieval |
| metrics | [repeated Metric](#metric) | Metrics which this monitor monitors |
| monitored | [repeated Monitored](#monitored) | Entity which is monitored |
| managed\_by\_rule | [ bool](#bool) | Whether this monitor is governed by a deployment rule. When true, the monitor's configuration is managed centrally by a rule and manual edits may be overwritten the next time the rule is applied. False for monitors that are configured individually. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Human-provided description of what this monitor checks. Unset for monitor types that don't carry one. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sql\_source.sql\_source | [optional string](#string) | SQL query used as the data source instead of querying the monitored table or view directly, when the monitor is configured with one. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | When set, the monitor has been deleted: it no longer runs, no longer reports metrics, and is excluded from every listing. The record is still served by id so a stored or stale identifier resolves to something, which makes this the only signal that the monitor is gone. Unset for a live monitor. |
### MonitorValue
(synq.monitors.info.v1.MonitorValue)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | none |
### MonitorVolume
(synq.monitors.info.v1.MonitorVolume)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) volume\_source.expression | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) volume\_source.count\_rows | [ CountRows](#countrows) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) volume\_source.table\_metadata | [ MetadataSource](#metadatasource) | none |
### Monitored
(synq.monitors.info.v1.Monitored)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the monitored entity. |
| synq\_path | [ string](#string) | Coalesce Quality Path to the monitored entity. |
| name | [ string](#string) | Name of the monitored entity |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Description of the monitored entity |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_integration\_id.integration\_id | [optional string](#string) | Id of the integration which provided information about monitored entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_object\_fqn.object\_fqn | [optional DatabaseFqn](#databasefqn) | Coordinates to the physical table/view which is monitored (when applicable) |
| sql\_dialect | [ SqlDialect](#sqldialect) | SQL dialect used for monitoring |
### Partitioning
(synq.monitors.info.v1.Partitioning)
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | none |
| period | [ Duration](#duration) | none |
### Segmentation
(synq.monitors.info.v1.Segmentation)
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | none |
### MonitorInfoRequest
(synq.monitors.info.v1.MonitorInfoRequest)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the monitor, or of a monitor-like entity such as a dbt test or a SQL test. Its `synq_path` spelling also accepts a custom monitor's own id, in either of the forms `monitor_id` takes. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_monitor\_id.monitor\_id | [optional string](#string) | Id of a custom monitor, as `synq.monitors.custom_monitors.v1.ListMonitors` returns it and `BatchUpdateMonitor` takes it. Accepts the bare UUID or the `custom-` form. An alternative to `id` rather than an addition to it: setting both is an error. |
### MonitorInfoResponse
(synq.monitors.info.v1.MonitorInfoResponse)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_monitor\_info.monitor\_info | [optional MonitorInfo](#monitorinfo) | none |
### AutomatedMonitorSettings
(synq.monitors.automated\_monitors.v1.AutomatedMonitorSettings)
| Field | Type | Description |
| - | - | - |
| severity | [ Severity](#severity) | none |
| sensitivity | [ Sensitivity](#sensitivity) | LEGACY - global sensitivity |
| metric\_ids | [repeated MetricId](#metricid) | LEGACY - which metrics to monitor |
| metric\_options | [repeated MetricOption](#metricoption) | NEW - per-metric sensitivity |
### CustomMonitorSettingsChange
(synq.monitors.automated\_monitors.v1.CustomMonitorSettingsChange)
| Field | Type | Description |
| - | - | - |
| monitored\_asset\_path | [ string](#string) | none |
| original\_settings | [ AutomatedMonitorSettings](#automatedmonitorsettings) | none |
| new\_settings | [ AutomatedMonitorSettings](#automatedmonitorsettings) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_resolved\_freshness\_field.resolved\_freshness\_field | [optional string](#string) | none |
### EntitySelectionQuery
(synq.monitors.automated\_monitors.v1.EntitySelectionQuery)
| Field | Type | Description |
| - | - | - |
| parts | [repeated QueryPart](#querypart) | none |
| operand | [ QueryOperand](#queryoperand) | none |
### EntitySelectionQuery.QueryPart
(synq.monitors.automated\_monitors.v1.EntitySelectionQuery.QueryPart)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier\_list | [ IdentifierList](#identifierlist) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_name\_search | [ WithNameSearch](#withnamesearch) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_type | [ WithType](#withtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_annotation | [ WithAnnotation](#withannotation) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_folder | [ InFolder](#infolder) | synq.queries.v1.InDataPlatform in\_data\_platform = 5; |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.entity\_ids | [ EntityIds](#entityids) | Deploy monitors on specific entities, named by the ids the API returned for them. Read back as `identifier_list`, with each id on the identifier it names. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ EntitySelectionQuery](#entityselectionquery) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.unsupported | [ Unsupported](#unsupported) | none |
### ExclusiveQueryConfig
(synq.monitors.automated\_monitors.v1.ExclusiveQueryConfig)
| Field | Type | Description |
| - | - | - |
| query | [ EntitySelectionQuery](#entityselectionquery) | none |
| resolver\_ql | [ string](#string) | ResolverQL selection string for the exclusion set. Same semantics as QueryConfig.resolver\_ql: when set on write it takes precedence over `query` and is stored compiled; on read it carries the canonical rendering of the stored selection, equal to `rendered_resolver_ql`. `query` is always populated. |
| rendered\_resolver\_ql | [ string](#string) | Canonical ResolverQL rendering of the exclusion selection. Same semantics as QueryConfig.rendered\_resolver\_ql: OUTPUT ONLY, populated for every rule, ignored on write. |
### ExclusiveStaticConfig
(synq.monitors.automated\_monitors.v1.ExclusiveStaticConfig)
| Field | Type | Description |
| - | - | - |
| path | [ string](#string) | none |
### MetricOption
(synq.monitors.automated\_monitors.v1.MetricOption)
| Field | Type | Description |
| - | - | - |
| sensitivity | [ Sensitivity](#sensitivity) | none |
| metric\_id | [ MetricId](#metricid) | none |
### MonitorSettingsChange
(synq.monitors.automated\_monitors.v1.MonitorSettingsChange)
| Field | Type | Description |
| - | - | - |
| monitored\_asset\_path | [ string](#string) | none |
| original\_settings | [ AutomatedMonitorSettings](#automatedmonitorsettings) | none |
| new\_settings | [ AutomatedMonitorSettings](#automatedmonitorsettings) | none |
### MonitorsDeploymentOverview
(synq.monitors.automated\_monitors.v1.MonitorsDeploymentOverview)
| Field | Type | Description |
| - | - | - |
| monitors\_to\_create\_for | [repeated string](#string) | none |
| monitors\_to\_delete\_for | [repeated string](#string) | none |
| active\_monitors\_for | [repeated string](#string) | none |
| monitor\_settings\_changes | [repeated MonitorSettingsChange](#monitorsettingschange) | none |
| custom\_monitors\_to\_create\_for | [repeated string](#string) | none |
| custom\_monitors\_to\_delete\_for | [repeated string](#string) | none |
| active\_custom\_monitors\_for | [repeated string](#string) | none |
| custom\_monitor\_settings\_changes | [repeated CustomMonitorSettingsChange](#custommonitorsettingschange) | none |
| skipped\_assets | [repeated SkippedAsset](#skippedasset) | none |
| overlapping\_rule\_ids | [repeated string](#string) | Deployment rules OTHER than the one this overview is for that also govern the assets it leaves monitored (the ones in active\_monitors\_for / active\_custom\_monitors\_for). Exact and complete: it is read off the same rule/asset rows the overview itself is computed from, so no per-asset lookup can improve on it. Empty when no other rule covers those assets — and note that a rule whose contribution is shadowed instead (SkippedAsset.reason SKIPPED\_HIGHER\_PRIORITY\_RULE) is not here; those assets are not left monitored by this rule at all. |
Ids only. Read their titles, owners and types with ListDeploymentRules(rule\_ids). |
### MonitorsDeploymentRule
(synq.monitors.automated\_monitors.v1.MonitorsDeploymentRule)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| title | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.query\_config | [ QueryConfig](#queryconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_query\_config | [ ExclusiveQueryConfig](#exclusivequeryconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.static\_config | [ StaticConfig](#staticconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_static\_config | [ ExclusiveStaticConfig](#exclusivestaticconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.product\_config | [ ProductConfig](#productconfig) | none |
| source | [ Source](#source) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID this rule was authored from, when deployed via configuration as code. Unset for rules authored directly through this API. |
On a write this field is only ever set, never unset: omitting it keeps the stored value, so a full-replace update cannot detach the rule from the config that owns it. An empty string is a real config id — the one a deploy that declares no namespace uses — so sending it attaches the rule to that config rather than clearing the field. No value detaches a rule; recreating it is the only way to hand it back to the app. |
### MonitorsDeploymentRuleView
(synq.monitors.automated\_monitors.v1.MonitorsDeploymentRuleView)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| title | [ string](#string) | none |
| monitor\_count | [ int32](#int32) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.query\_config | [ QueryConfig](#queryconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_query\_config | [ ExclusiveQueryConfig](#exclusivequeryconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.static\_config | [ StaticConfig](#staticconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_static\_config | [ ExclusiveStaticConfig](#exclusivestaticconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.product\_config | [ ProductConfig](#productconfig) | none |
| source | [ Source](#source) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | none |
### ProductConfig
(synq.monitors.automated\_monitors.v1.ProductConfig)
| Field | Type | Description |
| - | - | - |
| product\_id | [ string](#string) | none |
| severity | [ Severity](#severity) | none |
| sensitivity | [ Sensitivity](#sensitivity) | none |
| metric\_ids | [repeated MetricId](#metricid) | none |
| upstream | [ ProductConfigUpstreamStrategy](#productconfigupstreamstrategy) | none |
| self | [ ProductConfigSelfStrategy](#productconfigselfstrategy) | none |
| keep\_deleted\_monitors | [ bool](#bool) | none |
| metric\_options | [repeated MetricOption](#metricoption) | NEW. Returned sorted by metric\_id — a client can rely on that ordering rather than re-sorting. |
| delay\_model | [ DelayModel](#delaymodel) | How the delay and volume\_change\_delay metrics model the expected arrival time of new data. Unset leaves the stored value unchanged. |
### ProductConfigSelfStrategy
(synq.monitors.automated\_monitors.v1.ProductConfigSelfStrategy)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.all | [ ProductConfigSelfStrategyAll](#productconfigselfstrategyall) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.selection | [ ProductConfigSelfStrategySelection](#productconfigselfstrategyselection) | none |
### ProductConfigSelfStrategyAll
(synq.monitors.automated\_monitors.v1.ProductConfigSelfStrategyAll)
### ProductConfigSelfStrategySelection
(synq.monitors.automated\_monitors.v1.ProductConfigSelfStrategySelection)
| Field | Type | Description |
| - | - | - |
| query | [ EntitySelectionQuery](#entityselectionquery) | none |
### ProductConfigUpstreamStrategy
(synq.monitors.automated\_monitors.v1.ProductConfigUpstreamStrategy)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.all | [ ProductConfigUpstreamStrategyAll](#productconfigupstreamstrategyall) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.sources | [ ProductConfigUpstreamStrategySources](#productconfigupstreamstrategysources) | none |
### ProductConfigUpstreamStrategyAll
(synq.monitors.automated\_monitors.v1.ProductConfigUpstreamStrategyAll)
### ProductConfigUpstreamStrategySources
(synq.monitors.automated\_monitors.v1.ProductConfigUpstreamStrategySources)
### QueryConfig
(synq.monitors.automated\_monitors.v1.QueryConfig)
| Field | Type | Description |
| - | - | - |
| query | [ EntitySelectionQuery](#entityselectionquery) | none |
| severity | [ Severity](#severity) | none |
| sensitivity | [ Sensitivity](#sensitivity) | none |
| metric\_ids | [repeated MetricId](#metricid) | none |
| keep\_deleted\_monitors | [ bool](#bool) | none |
| metric\_options | [repeated MetricOption](#metricoption) | NEW - optional per-metric config. Returned sorted by metric\_id — a client can rely on that ordering rather than re-sorting. |
| resolver\_ql | [ string](#string) | ResolverQL selection string — an advanced, more expressive alternative to `query`. ResolverQL is a superset of the structured `query`: use it for selections `query` cannot express (e.g. lineage walks, column predicates). When set (non-empty) on write, `resolver_ql` is the source of truth and the structured `query` is ignored. The selection is compiled when it is written and stored in that form, so the text itself is not kept: two spellings of the same selection store the same rule, and a type alias such as "table" is expanded to the types it names at the time of the write. On read this field carries the canonical rendering of the stored selection, equal to `rendered_resolver_ql`, whichever way the rule was written, and is empty when the selection has no ResolverQL representation. Writing a read response back unchanged stores the same selection. `query` is always populated. |
| rendered\_resolver\_ql | [ string](#string) | Canonical ResolverQL rendering of this rule's selection, populated for every query rule regardless of how it was authored. OUTPUT ONLY — it is ignored on write; set `resolver_ql` to author a selection in ResolverQL. Empty only for selections that have no ResolverQL representation. Compare selections by this field rather than by the text a rule was written with, which may be spelled differently. |
| delay\_model | [ DelayModel](#delaymodel) | How the delay and volume\_change\_delay metrics model the expected arrival time of new data. Unset leaves the stored value unchanged. |
### SkippedAsset
(synq.monitors.automated\_monitors.v1.SkippedAsset)
| Field | Type | Description |
| - | - | - |
| asset\_path | [ string](#string) | none |
| reason | [ string](#string) | Why the asset was skipped, as a stable SCREAMING\_SNAKE token. Clients branch on these, so treat the spellings as part of this API: adding a value is safe, renaming one is a breaking change. A value not listed here may appear — a client must fall through to "skipped, reason unrecognised" rather than assume the set is closed. |
SKIPPED\_HIGHER\_PRIORITY\_RULE another rule of higher precedence governs the asset, so this rule's settings never reach it (see rule precedence) SKIPPED\_NO\_FRESHNESS\_COLUMN\_MATCH no column matched the freshness field SKIPPED\_NO\_FRESHNESS\_FIELD\_PATTERN no freshness field pattern is configured SKIPPED\_NO\_INTEGRATION the asset's integration is gone SKIPPED\_TEST\_PREPARATION\_FAILED the test could not be prepared SKIPPED\_TEST\_OWNED\_BY\_OTHER\_RULE another rule already deploys that test
A string rather than an enum for compatibility: an enum would have to be introduced as a second field, since the producers already write these tokens. |
\| reason\_code | [ SkipReason](#skipreason) | Machine-checkable form of `reason`, so a client can branch on a value the compiler knows instead of string-matching a wire constant. `reason` remains authoritative and keeps being written unchanged; `reason_code` is SKIP\_REASON\_UNSPECIFIED for a `reason` this field does not yet cover — fall through to "skipped, reason unrecognised" rather than treat that as impossible, since the set of reasons is expected to grow. |
### StaticConfig
(synq.monitors.automated\_monitors.v1.StaticConfig)
| Field | Type | Description |
| - | - | - |
| path | [ string](#string) | none |
| severity | [ Severity](#severity) | none |
| sensitivity | [ Sensitivity](#sensitivity) | none |
| metric\_ids | [repeated MetricId](#metricid) | none |
| metric\_options | [repeated MetricOption](#metricoption) | NEW. Returned sorted by metric\_id — a client can rely on that ordering rather than re-sorting. |
| delay\_model | [ DelayModel](#delaymodel) | How the delay and volume\_change\_delay metrics model the expected arrival time of new data. Unset leaves the stored value unchanged. |
### BatchDeleteDeploymentRulesRequest
(synq.monitors.automated\_monitors.v1.BatchDeleteDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | IDs of the DeploymentRules to delete. |
### BatchDeleteDeploymentRulesResponse
(synq.monitors.automated\_monitors.v1.BatchDeleteDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deleted\_ids | [repeated string](#string) | IDs for which DeploymentRules were deleted without error. |
| errors | [repeated DeleteError](#deleteerror) | Errors raised while deleting. This list will be empty if there were no errors. |
### BatchDeleteDeploymentRulesResponse.DeleteError
(synq.monitors.automated\_monitors.v1.BatchDeleteDeploymentRulesResponse.DeleteError)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchDeployDeploymentRulesRequest
(synq.monitors.automated\_monitors.v1.BatchDeployDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated MonitorsDeploymentRule](#monitorsdeploymentrule) | List of DeploymentRules to deploy. |
### BatchDeployDeploymentRulesResponse
(synq.monitors.automated\_monitors.v1.BatchDeployDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deployed\_ids | [repeated string](#string) | IDs for which DeploymentRules were deployed without error. |
| errors | [repeated DeployError](#deployerror) | Errors raised while deploying. This list will be empty if there were no errors. |
### BatchDeployDeploymentRulesResponse.DeployError
(synq.monitors.automated\_monitors.v1.BatchDeployDeploymentRulesResponse.DeployError)
Details of errors encountered during the deploy operation.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchGetDeploymentRulesRequest
(synq.monitors.automated\_monitors.v1.BatchGetDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### BatchGetDeploymentRulesResponse
(synq.monitors.automated\_monitors.v1.BatchGetDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [map DeploymentRulesEntry](#deploymentrulesentry) | none |
### BatchGetDeploymentRulesResponse.DeploymentRulesEntry
(synq.monitors.automated\_monitors.v1.BatchGetDeploymentRulesResponse.DeploymentRulesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ MonitorsDeploymentRuleView](#monitorsdeploymentruleview) | none |
### BatchUpdateDeploymentRuleTitleRequest
(synq.monitors.automated\_monitors.v1.BatchUpdateDeploymentRuleTitleRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated UpdateDeploymentRuleTitleRequest](#updatedeploymentruletitlerequest) | none |
### BatchUpdateDeploymentRuleTitleResponse
(synq.monitors.automated\_monitors.v1.BatchUpdateDeploymentRuleTitleResponse)
| Field | Type | Description |
| - | - | - |
| updated\_ids | [repeated string](#string) | IDs of the deployment rules whose title was updated. Optional and additive: an older server leaves it unset even though it did rename the rules. |
| errors | [repeated UpdateError](#updateerror) | One entry per requested deployment rule that was not updated, most commonly because no rule with that id exists. Empty when every requested rule was updated. A rule missing from both lists was not renamed. |
### BatchUpdateDeploymentRuleTitleResponse.UpdateError
(synq.monitors.automated\_monitors.v1.BatchUpdateDeploymentRuleTitleResponse.UpdateError)
Details of a requested deployment rule that was not updated.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchUpsertDeploymentRulesRequest
(synq.monitors.automated\_monitors.v1.BatchUpsertDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated MonitorsDeploymentRule](#monitorsdeploymentrule) | List of DeploymentRules to upsert. |
### BatchUpsertDeploymentRulesResponse
(synq.monitors.automated\_monitors.v1.BatchUpsertDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| created\_ids | [repeated string](#string) | IDs for which DeploymentRules were created without error. |
| updated\_ids | [repeated string](#string) | IDs for which DeploymentRules were updated without error. |
| errors | [repeated UpsertError](#upserterror) | Errors raised while upsert. This list will be empty if there were no errors. |
### BatchUpsertDeploymentRulesResponse.UpsertError
(synq.monitors.automated\_monitors.v1.BatchUpsertDeploymentRulesResponse.UpsertError)
Details of errors encountered during the upsert operation.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### ConfigsFilter
(synq.monitors.automated\_monitors.v1.ConfigsFilter)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### DeleteDeploymentRuleRequest
(synq.monitors.automated\_monitors.v1.DeleteDeploymentRuleRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
### DeleteDeploymentRuleResponse
(synq.monitors.automated\_monitors.v1.DeleteDeploymentRuleResponse)
### DeployDeploymentRuleRequest
(synq.monitors.automated\_monitors.v1.DeployDeploymentRuleRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rule | [ MonitorsDeploymentRule](#monitorsdeploymentrule) | none |
### DeployDeploymentRuleResponse
(synq.monitors.automated\_monitors.v1.DeployDeploymentRuleResponse)
| Field | Type | Description |
| - | - | - |
| overview | [ MonitorsDeploymentOverview](#monitorsdeploymentoverview) | What the deploy did: the monitors it created, removed and left in place, the surviving monitors whose settings it changed, and the assets it skipped. |
Computed inside this call, before the write, from the same overview the deploy acted on — so unlike an overview a caller fetches for itself, no decision window separates it from what happened. Optional and additive: an older server leaves it unset, and a caller must then answer from what it knew before the call rather than treat the absence as an error. |
### GetDeployDeleteOverviewRequest
(synq.monitors.automated\_monitors.v1.GetDeployDeleteOverviewRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
### GetDeployDeleteOverviewResponse
(synq.monitors.automated\_monitors.v1.GetDeployDeleteOverviewResponse)
| Field | Type | Description |
| - | - | - |
| overview | [ MonitorsDeploymentOverview](#monitorsdeploymentoverview) | none |
### GetDeployOverviewRequest
(synq.monitors.automated\_monitors.v1.GetDeployOverviewRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rule | [ MonitorsDeploymentRule](#monitorsdeploymentrule) | none |
### GetDeployOverviewResponse
(synq.monitors.automated\_monitors.v1.GetDeployOverviewResponse)
| Field | Type | Description |
| - | - | - |
| overview | [ MonitorsDeploymentOverview](#monitorsdeploymentoverview) | none |
### ListDeploymentRulesForAssetRequest
(synq.monitors.automated\_monitors.v1.ListDeploymentRulesForAssetRequest)
| Field | Type | Description |
| - | - | - |
| asset\_path | [ string](#string) | none |
### ListDeploymentRulesForAssetResponse
(synq.monitors.automated\_monitors.v1.ListDeploymentRulesForAssetResponse)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated MonitorsDeploymentRuleView](#monitorsdeploymentruleview) | none |
### ListDeploymentRulesRequest
(synq.monitors.automated\_monitors.v1.ListDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| sources | [repeated Source](#source) | none |
| config\_ids | [repeated string](#string) | none |
| configs\_filter | [ ConfigsFilter](#configsfilter) | Filter SqlTests by config IDs. Pass `ConfigsFilter{ids: ["config1", "config2"]}` to filter by specific configs. If nil, no config\_id filtering is applied. If empty, the one with NULL config\_id will be excluded. |
| rule\_ids | [repeated string](#string) | none |
| rule\_types | [repeated DeploymentRuleType](#deploymentruletype) | Filter by rule type. When empty, rules of every type are returned. When set, only rules whose type is listed are returned (e.g. pass \[DEPLOYMENT\_RULE\_TYPE\_QUERY, DEPLOYMENT\_RULE\_TYPE\_PRODUCT] to exclude single-path rules). The filter is applied server-side before paging, so every page reflects only the selected types. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. An unset/zero page\_size applies the server default; the server never returns all rules in one response. Pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListDeploymentRulesResponse
(synq.monitors.automated\_monitors.v1.ListDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated MonitorsDeploymentRuleView](#monitorsdeploymentruleview) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. This response carries at most one bounded page of rules, never the full set. The cursor is the paging contract: to retrieve every rule, repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty. page\_info.count is the number of rules in this page. page\_info.total\_count is a best-effort total for the same filter, but it is deprecated — do not drive paging off it; loop on last\_id instead. |
### UpdateDeploymentRuleTitleRequest
(synq.monitors.automated\_monitors.v1.UpdateDeploymentRuleTitleRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| title | [ string](#string) | none |
### UpsertDeploymentRuleRequest
(synq.monitors.automated\_monitors.v1.UpsertDeploymentRuleRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rule | [ MonitorsDeploymentRule](#monitorsdeploymentrule) | none |
### UpsertDeploymentRuleResponse
(synq.monitors.automated\_monitors.v1.UpsertDeploymentRuleResponse)
### BatchUpdateMonitorSegmentLabelsRequest
(synq.monitors.segments.v1.BatchUpdateMonitorSegmentLabelsRequest)
| Field | Type | Description |
| - | - | - |
| segments | [repeated SegmentLabel](#segmentlabel) | The segments to name. Each entry replaces that segment's label outright; a segment not listed is left alone. |
### BatchUpdateMonitorSegmentLabelsRequest.SegmentLabel
(synq.monitors.segments.v1.BatchUpdateMonitorSegmentLabelsRequest.SegmentLabel)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the segment to name, as returned by ListMonitorSegments. |
| label | [ string](#string) | The name to give the segment. An empty string clears the label, so the segment falls back to showing its raw value. |
### BatchUpdateMonitorSegmentLabelsResponse
(synq.monitors.segments.v1.BatchUpdateMonitorSegmentLabelsResponse)
| Field | Type | Description |
| - | - | - |
| segments | [repeated MonitorSegment](#monitorsegment) | The segments that were updated, in their state after the update. |
| errors | [repeated UpdateError](#updateerror) | One entry per requested segment that was not updated, most commonly because no segment with that id has been ingested. Empty when every requested segment was updated. |
### BatchUpdateMonitorSegmentLabelsResponse.UpdateError
(synq.monitors.segments.v1.BatchUpdateMonitorSegmentLabelsResponse.UpdateError)
Details of a requested segment that was not updated.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | The id as it was requested. |
| reason | [ string](#string) | none |
### ListMonitorSegmentsRequest
(synq.monitors.segments.v1.ListMonitorSegmentsRequest)
| Field | Type | Description |
| - | - | - |
| monitor\_id | [ string](#string) | The monitor whose segments to list: either a custom monitor id, or the Coalesce Quality path of the monitor. Both are accepted wherever this API addresses a monitor. |
### ListMonitorSegmentsResponse
(synq.monitors.segments.v1.ListMonitorSegmentsResponse)
| Field | Type | Description |
| - | - | - |
| segments | [repeated MonitorSegment](#monitorsegment) | The monitor's segments, ordered by name. Empty when the monitor is not segmented, or has not ingested any data yet. |
### MonitorSegment
(synq.monitors.segments.v1.MonitorSegment)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Identifier of this segment. Opaque: pass it back to BatchUpdateMonitorSegmentLabels to name the segment, or to synq.monitors.predictions.v1.MonitorPredictionsService as synq\_path to read this segment's predictions. Do not construct or parse it. |
| monitor\_id | [ string](#string) | Id of the monitor this segment belongs to, as accepted by synq.monitors.custom\_monitors.v1.CustomMonitorsService. |
| segmentation\_expression | [ string](#string) | The monitor's segmentation expression: the column or expression whose distinct values produce the segments, for example "country". |
| value | [ string](#string) | The value of the segmentation expression this segment covers, exactly as it was read from the monitored data. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_label.label | [optional string](#string) | The name given to this segment. Unset when the segment has never been named, in which case every surface shows `value` instead. |
| name | [ string](#string) | What this segment is shown as: `label` when one is set, `value` otherwise. This is the name that reaches check names, search and alerts. |
| created\_at | [ Timestamp](#timestamp) | When this segment was first observed in the monitored data. |
| last\_ingested\_at | [ Timestamp](#timestamp) | When this segment was last observed in the monitored data. A segment whose value stops appearing keeps its last observation time until the monitor drops it. |
### Prediction
(synq.monitors.history.v1.Prediction)
| Field | Type | Description |
| - | - | - |
| f | [ string](#string) | Field |
| s | [ string](#string) | Segment |
| m | [ string](#string) | Metric |
| v | [ double](#double) | Value |
| vu | [ DoubleValue](#doublevalue) | Upper bound |
| vl | [ DoubleValue](#doublevalue) | Lower bound |
| p | [ string](#string) | Pattern |
| e | [ double](#double) | Expected value |
| st | [ double](#double) | Standard deviation |
| t | [ Timestamp](#timestamp) | Timestamp |
### HistoryRequest
(synq.monitors.history.v1.HistoryRequest)
| Field | Type | Description |
| - | - | - |
| monitor\_path | [ string](#string) | Monitor synq path (e.g. monitor::integration::db::schema::table::volume). REST clients pass this as the `monitorPath` query parameter. |
| metrics\_version | [ int32](#int32) | none |
| segments | [repeated string](#string) | none |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
### HistoryResponse
(synq.monitors.history.v1.HistoryResponse)
| Field | Type | Description |
| - | - | - |
| predictions | [repeated Prediction](#prediction) | none |
### BatchGetLatestEntityMetricsRequest
(synq.monitors.predictions.v1.BatchGetLatestEntityMetricsRequest)
| Field | Type | Description |
| - | - | - |
| synq\_paths | [repeated string](#string) | Entity synq\_paths to look up. Duplicates are allowed and de-duplicated server-side. |
### BatchGetLatestEntityMetricsResponse
(synq.monitors.predictions.v1.BatchGetLatestEntityMetricsResponse)
| Field | Type | Description |
| - | - | - |
| latest\_by\_synq\_path | [map LatestBySynqPathEntry](#latestbysynqpathentry) | Latest observed data point keyed by the requested synq\_path. Entities with no recent sample are omitted — callers should treat a missing key as "no metrics available". |
### BatchGetLatestEntityMetricsResponse.LatestBySynqPathEntry
(synq.monitors.predictions.v1.BatchGetLatestEntityMetricsResponse.LatestBySynqPathEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ EntityMetricDataPoint](#entitymetricdatapoint) | none |
### GetEntityMetricsRequest
(synq.monitors.predictions.v1.GetEntityMetricsRequest)
| Field | Type | Description |
| - | - | - |
| synq\_path | [ string](#string) | Entity synq\_path (table, view, or dbt model). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_from.from | [optional Timestamp](#timestamp) | Start of time range. Defaults to 30 days before `to`. Max lookback: 30 days. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_to.to | [optional Timestamp](#timestamp) | End of time range. Defaults to now. |
### GetEntityMetricsResponse
(synq.monitors.predictions.v1.GetEntityMetricsResponse)
| Field | Type | Description |
| - | - | - |
| data\_points | [repeated EntityMetricDataPoint](#entitymetricdatapoint) | none |
### GetMonitorPredictionsRequest
(synq.monitors.predictions.v1.GetMonitorPredictionsRequest)
| Field | Type | Description |
| - | - | - |
| synq\_path | [ string](#string) | Monitor to read. Accepts the monitor's synq\_path, the `::seg-` synq\_path of one of its segments, or — for a custom monitor — its own id as `synq.monitors.custom_monitors.v1.ListMonitors` returns it: the bare UUID or the `custom-` form. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_metrics\_version.metrics\_version | [optional int32](#int32) | Metrics version. Defaults to the monitor's current version. |
| segments | [repeated string](#string) | Segment filters. If empty, all segments are returned. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_from.from | [optional Timestamp](#timestamp) | Start of time range. Defaults to 30 days before `to`. Max lookback: 30 days. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_to.to | [optional Timestamp](#timestamp) | End of time range. Defaults to now. |
### GetMonitorPredictionsResponse
(synq.monitors.predictions.v1.GetMonitorPredictionsResponse)
| Field | Type | Description |
| - | - | - |
| predictions | [repeated MonitorPrediction](#monitorprediction) | none |
### EntityMetricDataPoint
(synq.monitors.predictions.v1.EntityMetricDataPoint)
Single observation of table-level metrics collected from the data warehouse.
| Field | Type | Description |
| - | - | - |
| synq\_path | [ string](#string) | Entity synq\_path. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_num\_rows.num\_rows | [optional Int64Value](#int64value) | Row count. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_loaded\_at.last\_loaded\_at | [optional Timestamp](#timestamp) | Last loaded time as reported by the data warehouse. |
| scheduled\_at | [ Timestamp](#timestamp) | When the metric was scheduled for collection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_snapshotted\_at.snapshotted\_at | [optional Timestamp](#timestamp) | Data warehouse snapshot time. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_size\_bytes.size\_bytes | [optional Int64Value](#int64value) | Physical size in bytes as reported by the data warehouse. |
### MonitorPrediction
(synq.monitors.predictions.v1.MonitorPrediction)
Single prediction data point from the anomaly detection model.
| Field | Type | Description |
| - | - | - |
| field | [ string](#string) | Monitored field. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_segment.segment | [optional string](#string) | Segment. |
| metric\_id | [ string](#string) | Metric identifier. |
| value | [ double](#double) | Observed value. |
| expected | [ double](#double) | Model's expected value. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_stddev.stddev | [optional double](#double) | Prediction standard deviation. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_upper\_bound.upper\_bound | [optional DoubleValue](#doublevalue) | Confidence band upper bound. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_lower\_bound.lower\_bound | [optional DoubleValue](#doublevalue) | Confidence band lower bound. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pattern.pattern | [optional string](#string) | Anomaly pattern (e.g., "spike", "drop"). |
| scheduled\_at | [ Timestamp](#timestamp) | When the prediction was scheduled. |
| is\_corrected | [ bool](#bool) | Whether a user has marked this prediction as expected behavior. |
### CronSchedule
(synq.schedule.v1.CronSchedule)
CronSchedule uses standard cron expression syntax.
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | Standard cron expression (e.g. "0 \*/6 \* \* \*"). |
### ICalSchedule
(synq.schedule.v1.ICalSchedule)
ICalSchedule uses RFC 5545 RRULE recurrence rules.
| Field | Type | Description |
| - | - | - |
| recurrence\_rule | [ string](#string) | RFC 5545 RRULE recurrence rule (e.g. "FREQ=MONTHLY;BYDAY=TH;BYSETPOS=-1"). DTSTART should be set via the dtstart field below rather than inline in the rule. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_dtstart.dtstart | [optional Timestamp](#timestamp) | Anchor time for the recurrence rule. Wall-clock time interpreted in the schedule's timezone. Defines when the schedule starts and aligns occurrences. If not set and no DTSTART is present in the recurrence\_rule, defaults to now(). |
### Schedule
(synq.schedule.v1.Schedule)
Schedule defines a recurring schedule with timezone context.
| Field | Type | Description |
| - | - | - |
| timezone | [ string](#string) | IANA timezone, e.g. "Europe/Warsaw", "America/New\_York". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.cron | [ CronSchedule](#cronschedule) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.ical | [ ICalSchedule](#icalschedule) | none |
### FixedQuantumGrid
(synq.schedule.v1.FixedQuantumGrid)
FixedQuantumGrid snaps onto a fixed-size grid anchored at the Unix epoch
(1970-01-01T00:00:00Z): \[epoch, epoch+quantum, epoch+2\*quantum, ...].
| Field | Type | Description |
| - | - | - |
| quantum | [ Duration](#duration) | Spacing between grid points, e.g. 10m. |
### IntegrationAnchorGrid
(synq.schedule.v1.IntegrationAnchorGrid)
IntegrationAnchorGrid snaps onto the shared, stable grid an integration's
periodic warehouse scrape already fires on. Aligning to it lets a run reuse
the same warehouse wake as the scrape.
The integration is resolved in the caller's own workspace (taken from the
authenticated identity, never from the request), and the caller must be
authorized to read it — a token without integration read access is rejected.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | The integration whose scrape grid to align to. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_period.period | [optional Duration](#duration) | Spacing between grid points. When unset, the integration's configured scrape cadence is used — this is what actually resumes the warehouse once per tick. Set it only to override for unusual cases. |
### PreviewScheduleParams
(synq.schedule.v1.PreviewScheduleParams)
Parameters for evaluating a single schedule.
| Field | Type | Description |
| - | - | - |
| schedule | [ Schedule](#schedule) | The schedule to evaluate. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_after.after | [optional Timestamp](#timestamp) | Starting point for computing occurrences. Defaults to now if not set. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_limit.limit | [optional int32](#int32) | Number of occurrences to return. Defaults to 7, maximum 100. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_snap.snap | [optional ScheduleSnap](#schedulesnap) | Optional. When set, each computed occurrence is mapped to the nearest matching grid point before being returned, so the job co-fires with existing activity (e.g. a warehouse that is already awake) instead of triggering its own resume. When unset, occurrences are returned exactly as the schedule expression produces them. |
### PreviewScheduleResult
(synq.schedule.v1.PreviewScheduleResult)
Result for a single schedule evaluation.
| Field | Type | Description |
| - | - | - |
| occurrences | [repeated Timestamp](#timestamp) | The next occurrences in chronological order. Empty if error is set. |
| error | [ string](#string) | Error message if the schedule expression is invalid or cannot be evaluated. Empty on success. |
| snap\_info | [repeated SnapInfo](#snapinfo) | Per-occurrence snap outcome, index-aligned with `occurrences`. Empty when snap was not requested. |
### PreviewSchedulesRequest
(synq.schedule.v1.PreviewSchedulesRequest)
| Field | Type | Description |
| - | - | - |
| schedules | [repeated PreviewScheduleParams](#previewscheduleparams) | Schedules to evaluate. An empty list returns an empty response. |
### PreviewSchedulesResponse
(synq.schedule.v1.PreviewSchedulesResponse)
| Field | Type | Description |
| - | - | - |
| results | [repeated PreviewScheduleResult](#previewscheduleresult) | Results matching the request schedules by index. |
### ScheduleSnap
(synq.schedule.v1.ScheduleSnap)
ScheduleSnap maps each schedule occurrence onto a grid so runs line up with
activity that is already happening, avoiding a separate warehouse resume.
Pick one grid source and, optionally, how far an occurrence may move.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) grid.integration\_anchor | [ IntegrationAnchorGrid](#integrationanchorgrid) | Snap onto the shared warehouse-scrape grid for an integration, so the run co-fires with the periodic scrape and reuses the same warehouse wake instead of paying for another resume. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) grid.fixed\_quantum | [ FixedQuantumGrid](#fixedquantumgrid) | Snap onto a fixed-quantum grid (e.g. every 10 minutes from the Unix epoch, UTC). Useful for tidy round-minute fire times without coupling to integration activity. |
| tolerance | [ Duration](#duration) | How far an occurrence may be moved to land on the grid. If the nearest eligible grid point is farther away than this, the occurrence is returned unmoved and flagged as not snapped (see SnapInfo). Unset or zero means an occurrence snaps only when it already lands exactly on a grid point. |
| direction | [ SnapDirection](#snapdirection) | Direction an occurrence is allowed to move. Unspecified behaves as NEAREST. |
### SnapInfo
(synq.schedule.v1.SnapInfo)
SnapInfo records, for one occurrence, whether snapping moved it onto the grid.
| Field | Type | Description |
| - | - | - |
| original | [ Timestamp](#timestamp) | The occurrence as the schedule expression produced it, before snapping. |
| snapped | [ bool](#bool) | True if the occurrence was moved onto the grid. False if the nearest grid point was farther than the tolerance (or none was available), in which case the aligned `occurrences` entry equals `original`. |
### Integration
(synq.extensions.atlan.integrations.v1.Integration)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | none |
| id | [ string](#string) | ID for the integration. Unique for a workspace. |
| atlan\_tenant\_url | [ string](#string) | none |
| atlan\_api\_token | [ string](#string) | none |
| is\_valid | [ bool](#bool) | Whether the integration is valid or not. Set on `upsert` as a result of connection to atlan api. |
| is\_active | [ bool](#bool) | Whether the integration is active or not. Only valid integrations can be activated. |
| created\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | none |
| updated\_at | [ Timestamp](#timestamp) | none |
### ActivateRequest
(synq.extensions.atlan.integrations.v1.ActivateRequest)
| Field | Type | Description |
| - | - | - |
| activate | [ bool](#bool) | none |
### ActivateResponse
(synq.extensions.atlan.integrations.v1.ActivateResponse)
### GetRequest
(synq.extensions.atlan.integrations.v1.GetRequest)
### GetResponse
(synq.extensions.atlan.integrations.v1.GetResponse)
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | none |
### PingRequest
(synq.extensions.atlan.integrations.v1.PingRequest)
| Field | Type | Description |
| - | - | - |
| atlan\_tenant\_url | [ string](#string) | none |
| atlan\_api\_token | [ string](#string) | none |
### PingResponse
(synq.extensions.atlan.integrations.v1.PingResponse)
| Field | Type | Description |
| - | - | - |
| success | [ bool](#bool) | none |
### RemoveRequest
(synq.extensions.atlan.integrations.v1.RemoveRequest)
### RemoveResponse
(synq.extensions.atlan.integrations.v1.RemoveResponse)
### UpsertRequest
(synq.extensions.atlan.integrations.v1.UpsertRequest)
| Field | Type | Description |
| - | - | - |
| atlan\_tenant\_url | [ string](#string) | none |
| atlan\_api\_token | [ string](#string) | none |
### UpsertResponse
(synq.extensions.atlan.integrations.v1.UpsertResponse)
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | none |
### GetAtlanConnectionsRequest
(synq.extensions.atlan.provider.v1.GetAtlanConnectionsRequest)
### GetAtlanConnectionsResponse
(synq.extensions.atlan.provider.v1.GetAtlanConnectionsResponse)
| Field | Type | Description |
| - | - | - |
| connections | [repeated AtlanConnection](#atlanconnection) | none |
### GetAtlanDataProductsRequest
(synq.extensions.atlan.provider.v1.GetAtlanDataProductsRequest)
### GetAtlanDataProductsResponse
(synq.extensions.atlan.provider.v1.GetAtlanDataProductsResponse)
| Field | Type | Description |
| - | - | - |
| data\_products | [repeated AtlanDataProduct](#atlandataproduct) | none |
### GetAtlanDomainsRequest
(synq.extensions.atlan.provider.v1.GetAtlanDomainsRequest)
### GetAtlanDomainsResponse
(synq.extensions.atlan.provider.v1.GetAtlanDomainsResponse)
| Field | Type | Description |
| - | - | - |
| domains | [repeated AtlanDataDomain](#atlandatadomain) | none |
### AtlanConnection
(synq.extensions.atlan.provider.v1.AtlanConnection)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| qualified\_name | [ string](#string) | none |
### AtlanDataDomain
(synq.extensions.atlan.provider.v1.AtlanDataDomain)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| qualified\_name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent.parent | [optional AtlanDataDomain](#atlandatadomain) | none |
### AtlanDataProduct
(synq.extensions.atlan.provider.v1.AtlanDataProduct)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| qualified\_name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_domain.domain | [optional AtlanDataDomain](#atlandatadomain) | none |
| owner\_users | [ Emails](#emails) | emails for the owner users |
| owner\_groups | [map OwnerGroupsEntry](#ownergroupsentry) | emails for the owner groups mapped against group ID |
### AtlanDataProduct.Emails
(synq.extensions.atlan.provider.v1.AtlanDataProduct.Emails)
| Field | Type | Description |
| - | - | - |
| emails | [repeated string](#string) | none |
### AtlanDataProduct.OwnerGroupsEntry
(synq.extensions.atlan.provider.v1.AtlanDataProduct.OwnerGroupsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Emails](#emails) | none |
### DomainMappingToggle
(synq.extensions.atlan.workflows.v1.DomainMappingToggle)
| Field | Type | Description |
| - | - | - |
| atlan\_domain\_qualified\_name | [ string](#string) | none |
### MappingToggle
(synq.extensions.atlan.workflows.v1.MappingToggle)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mapping.product\_mapping | [ ProductMappingToggle](#productmappingtoggle) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mapping.domain\_mapping | [ DomainMappingToggle](#domainmappingtoggle) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mapping.product\_owner\_mapping | [ ProductOwnerMappingToggle](#productownermappingtoggle) | none |
| enabled | [ bool](#bool) | none |
### ProductMappingToggle
(synq.extensions.atlan.workflows.v1.ProductMappingToggle)
| Field | Type | Description |
| - | - | - |
| atlan\_product\_qualified\_name | [ string](#string) | none |
### ProductOwnerMappingToggle
(synq.extensions.atlan.workflows.v1.ProductOwnerMappingToggle)
| Field | Type | Description |
| - | - | - |
| atlan\_product\_qualified\_name | [ string](#string) | none |
### FetchRunsRequest
(synq.extensions.atlan.workflows.v1.FetchRunsRequest)
| Field | Type | Description |
| - | - | - |
| from | [ int32](#int32) | none |
| limit | [ int32](#int32) | none |
### FetchRunsResponse
(synq.extensions.atlan.workflows.v1.FetchRunsResponse)
| Field | Type | Description |
| - | - | - |
| workflow\_runs | [repeated WorkflowRun](#workflowrun) | none |
### GetConnectionMappingsRequest
(synq.extensions.atlan.workflows.v1.GetConnectionMappingsRequest)
### GetConnectionMappingsResponse
(synq.extensions.atlan.workflows.v1.GetConnectionMappingsResponse)
| Field | Type | Description |
| - | - | - |
| mappings | [repeated ConnectionMapping](#connectionmapping) | none |
### GetDomainMappingsRequest
(synq.extensions.atlan.workflows.v1.GetDomainMappingsRequest)
### GetDomainMappingsResponse
(synq.extensions.atlan.workflows.v1.GetDomainMappingsResponse)
| Field | Type | Description |
| - | - | - |
| mappings | [repeated DomainMapping](#domainmapping) | none |
### GetMappingTogglesRequest
(synq.extensions.atlan.workflows.v1.GetMappingTogglesRequest)
### GetMappingTogglesResponse
(synq.extensions.atlan.workflows.v1.GetMappingTogglesResponse)
| Field | Type | Description |
| - | - | - |
| toggles | [repeated MappingToggle](#mappingtoggle) | none |
### GetProductMappingsRequest
(synq.extensions.atlan.workflows.v1.GetProductMappingsRequest)
### GetProductMappingsResponse
(synq.extensions.atlan.workflows.v1.GetProductMappingsResponse)
| Field | Type | Description |
| - | - | - |
| mappings | [repeated ProductMapping](#productmapping) | none |
### RemoveConnectionMappingsRequest
(synq.extensions.atlan.workflows.v1.RemoveConnectionMappingsRequest)
| Field | Type | Description |
| - | - | - |
| atlan\_connection\_qualified\_names | [repeated string](#string) | none |
### RemoveConnectionMappingsResponse
(synq.extensions.atlan.workflows.v1.RemoveConnectionMappingsResponse)
### SetConnectionMappingsRequest
(synq.extensions.atlan.workflows.v1.SetConnectionMappingsRequest)
| Field | Type | Description |
| - | - | - |
| mappings | [repeated ConnectionMapping](#connectionmapping) | none |
### SetConnectionMappingsResponse
(synq.extensions.atlan.workflows.v1.SetConnectionMappingsResponse)
### SetMappingTogglesRequest
(synq.extensions.atlan.workflows.v1.SetMappingTogglesRequest)
| Field | Type | Description |
| - | - | - |
| toggles | [repeated MappingToggle](#mappingtoggle) | none |
### SetMappingTogglesResponse
(synq.extensions.atlan.workflows.v1.SetMappingTogglesResponse)
### SynchronizeRequest
(synq.extensions.atlan.workflows.v1.SynchronizeRequest)
| Field | Type | Description |
| - | - | - |
| use\_v2 | [ bool](#bool) | none |
### SynchronizeResponse
(synq.extensions.atlan.workflows.v1.SynchronizeResponse)
| Field | Type | Description |
| - | - | - |
| workflow\_run | [ WorkflowRun](#workflowrun) | none |
| has\_errors | [ bool](#bool) | none |
### ConnectionMapping
(synq.extensions.atlan.workflows.v1.ConnectionMapping)
| Field | Type | Description |
| - | - | - |
| atlan\_connection\_qualified\_name | [ string](#string) | none |
| synq\_data\_platform\_identifier | [ DataPlatformIdentifier](#dataplatformidentifier) | none |
### DomainMapping
(synq.extensions.atlan.workflows.v1.DomainMapping)
| Field | Type | Description |
| - | - | - |
| atlan\_domain\_qualified\_name | [ string](#string) | none |
| synq\_domain\_id | [ string](#string) | none |
### ProductMapping
(synq.extensions.atlan.workflows.v1.ProductMapping)
| Field | Type | Description |
| - | - | - |
| atlan\_dataproduct\_qualified\_name | [ string](#string) | none |
| synq\_dataproduct\_identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
### CommandResult
(synq.extensions.atlan.workflows.v1.CommandResult)
| Field | Type | Description |
| - | - | - |
| command | [ string](#string) | none |
| status | [ Status](#status) | none |
| logs | [repeated string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_error.error | [optional string](#string) | none |
### Log
(synq.extensions.atlan.workflows.v1.Log)
| Field | Type | Description |
| - | - | - |
| message | [ string](#string) | none |
| timestamp | [ Timestamp](#timestamp) | none |
| status | [ Status](#status) | none |
| metadata | [map MetadataEntry](#metadataentry) | none |
### Log.MetadataEntry
(synq.extensions.atlan.workflows.v1.Log.MetadataEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### WorkflowRun
(synq.extensions.atlan.workflows.v1.WorkflowRun)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | none |
| results | [repeated CommandResult](#commandresult) | Deprecated: Use Log instead. |
| status | [ Status](#status) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| ended\_at | [ Timestamp](#timestamp) | none |
| logs | [repeated Log](#log) | none |
### AlertingTarget
(synq.alerts.v2.AlertingTarget)
AlertingTarget represents a destination where alert notifications will be sent.
Each target type has its own specific configuration requirements.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.slack | [ SlackTarget](#slacktarget) | Send alert to a Slack channel or user. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.email | [ EmailTarget](#emailtarget) | Send alert via email. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.owner | [ OwnerTarget](#ownertarget) | Send alert to the owner of the monitored entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.ms\_teams | [ MsTeamsTarget](#msteamstarget) | Send alert to Microsoft Teams channel or member. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.webhook | [ WebhookTarget](#webhooktarget) | Send alert via webhook integration. |
### EmailTarget
(synq.alerts.v2.EmailTarget)
EmailTarget configures alert delivery via email.
| Field | Type | Description |
| - | - | - |
| recipient\_emails | [repeated string](#string) | List of email addresses that will receive the alert notifications. Each email must be a valid email address format. |
### MsTeamsTarget
(synq.alerts.v2.MsTeamsTarget)
MsTeamsTarget configures alert delivery to Microsoft Teams.
Alerts are sent to a Microsoft Teams channel.
| Field | Type | Description |
| - | - | - |
| channel | [ string](#string) | Teams channel name or ID. |
### OwnerTarget
(synq.alerts.v2.OwnerTarget)
OwnerTarget configures alert delivery to the owner of the monitored entity.
This allows alerts to be automatically sent to whoever owns the data being monitored.
| Field | Type | Description |
| - | - | - |
| owner\_path | [ string](#string) | Path that identifies the ownership context for this target. This determines how the system will resolve the actual owner. |
### SlackTarget
(synq.alerts.v2.SlackTarget)
SlackTarget configures alert delivery to Slack.
Alerts are sent to a Slack channel.
| Field | Type | Description |
| - | - | - |
| channel | [ string](#string) | Slack channel name or ID (e.g., "#alerts" or "C1234567890"). |
### WebhookTarget
(synq.alerts.v2.WebhookTarget)
WebhookTarget configures alert delivery via webhook/hook integrations.
This allows integration with external systems and custom notification workflows.
| Field | Type | Description |
| - | - | - |
| webhook\_id | [ string](#string) | Unique identifier of the webhook to send alerts to. This references a pre-configured webhook in the system. |
### Alert
(synq.alerts.v2.Alert)
Alert represents the configuration for an alert.
An alert is exactly one concrete kind, distinguished by the `kind` oneof:
* `issue_lifecycle`: fires on the issues of selected entities.
* `schema_change`: fires on the schema changes of selected entities.
* `incident_lifecycle`: fires on incident state transitions.
The identity, template and ownership fields are shared; everything specific to
how an alert triggers and delivers lives on its kind message.
Incident-owner alerts (which notify each incident's owner) are not configurable
and are not represented here; they are turned on or off with
AlertsService.ToggleIncidentOwnerAlert.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Unique identifier for the alert config (system-generated). |
| name | [ string](#string) | Human-readable name for the alert configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fqn.fqn | [optional string](#string) | User-provided fully qualified name for the alert config. This is a unique identifier that users can specify to reference the alert. |
| created\_at | [ Timestamp](#timestamp) | Timestamp when the alert config was created. |
| is\_disabled | [ bool](#bool) | Whether the alert config is currently disabled. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_template.template | [optional AlertTemplate](#alerttemplate) | Optional custom template for the rendered alert message. Common to every alert kind. Currently applied only to issue alerts; on other kinds it is accepted and ignored until templating is supported for them. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.issue\_lifecycle | [ IssueLifecycleAlert](#issuelifecyclealert) | Fires on the issues of selected entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.schema\_change | [ SchemaChangeAlert](#schemachangealert) | Fires on the schema changes of selected entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.incident\_lifecycle | [ IncidentLifecycleAlert](#incidentlifecyclealert) | Fires on incident state transitions. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID this alert was deployed from through configuration as code, unset otherwise. Immutable after creation; an empty string is the config of a deploy that declares no namespace. To move an alert to another config, delete it and recreate it under the same `fqn`. |
| etag | [ string](#string) | Output-only opaque tag, changed by every write. Pass it back as `etag` on a write to guard against overwriting a concurrent change. |
### EmailCustomizationOptions
(synq.alerts.v2.EmailCustomizationOptions)
EmailCustomizationOptions customises the addresses used when an alert is
delivered by email. Unset addresses fall back to the workspace defaults.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_from.from | [optional EmailSender](#emailsender) | Sender address emails are sent from. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reply\_to.reply\_to | [optional EmailSender](#emailsender) | Reply-to address for emails. |
### EmailSender
(synq.alerts.v2.EmailSender)
EmailSender is an email address paired with an optional display name.
| Field | Type | Description |
| - | - | - |
| email | [ string](#string) | Email address. |
| name | [ string](#string) | Display name shown alongside the address. |
### IncidentLifecycleAlert
(synq.alerts.v2.IncidentLifecycleAlert)
IncidentLifecycleAlert fires when an incident transitions into one of the
selected states. It takes no entity selection.
| Field | Type | Description |
| - | - | - |
| targets | [repeated AlertingTarget](#alertingtarget) | Targets where alerts will be sent. At least one is required. |
| settings | [ IncidentLifecycleAlertSettings](#incidentlifecyclealertsettings) | Incident lifecycle alert settings. |
### IncidentLifecycleAlertSettings
(synq.alerts.v2.IncidentLifecycleAlertSettings)
IncidentLifecycleAlertSettings configures an alert that fires when an incident
enters one of the selected states.
| Field | Type | Description |
| - | - | - |
| states | [repeated State](#state) | Incident states whose transitions fire this alert; an empty list would create an alert that never fires. |
### IssueAlertSettings
(synq.alerts.v2.IssueAlertSettings)
IssueAlertSettings is the unified alert settings type for issue-based alerts.
| Field | Type | Description |
| - | - | - |
| severities | [repeated Severity](#severity) | The severity levels that should trigger this alert. |
| notify\_upstream | [ bool](#bool) | Whether to notify upstream dependencies when this alert triggers. |
| allow\_sql\_test\_audit\_link | [ bool](#bool) | Whether to allow SQL test audit links in alert notifications. |
| ongoing | [ OngoingAlertsStrategy](#ongoingalertsstrategy) | Strategy for handling ongoing/repeated alerts. |
| grouping | [ IssueGroupingStrategy](#issuegroupingstrategy) | Strategy for grouping issues in alert notifications. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_email\_customization\_options.email\_customization\_options | [optional EmailCustomizationOptions](#emailcustomizationoptions) | Optional customisation of the sender and reply-to addresses on email alerts. |
### IssueGroupingStrategy
(synq.alerts.v2.IssueGroupingStrategy)
IssueGroupingStrategy controls how issues are grouped for alerting purposes.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.no\_grouping | [ NoGrouping](#nogrouping) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.system\_detected | [ SystemDetected](#systemdetected) | none |
### IssueGroupingStrategy.NoGrouping
(synq.alerts.v2.IssueGroupingStrategy.NoGrouping)
NoGrouping - each issue triggers a separate alert.
### IssueGroupingStrategy.SystemDetected
(synq.alerts.v2.IssueGroupingStrategy.SystemDetected)
SystemDetected - the system automatically groups related issues.
### IssueLifecycleAlert
(synq.alerts.v2.IssueLifecycleAlert)
IssueLifecycleAlert fires on the issues of the entities its trigger selects.
| Field | Type | Description |
| - | - | - |
| trigger | [ Trigger](#trigger) | Selects the entities that can trigger this alert. |
| targets | [repeated AlertingTarget](#alertingtarget) | Targets where alerts will be sent. At least one is required. |
| settings | [ IssueAlertSettings](#issuealertsettings) | Issue alert settings. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_owner.owner | [optional Owner](#owner) | Optional owner information. The owner is fixed when the alert is created. |
### OngoingAlertsStrategy
(synq.alerts.v2.OngoingAlertsStrategy)
OngoingAlertsStrategy defines how to handle ongoing/repeated alerts.
This prevents alert spam by controlling notification frequency.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.disabled | [ Disabled](#disabled) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.stream | [ Stream](#stream) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.schedule | [ Schedule](#schedule) | none |
### OngoingAlertsStrategy.Disabled
(synq.alerts.v2.OngoingAlertsStrategy.Disabled)
Disabled strategy - no ongoing alerts will be sent after the initial alert.
No configuration needed - alerts are simply disabled after the first one.
### OngoingAlertsStrategy.Schedule
(synq.alerts.v2.OngoingAlertsStrategy.Schedule)
Schedule strategy - alerts are sent on a scheduled basis.
| Field | Type | Description |
| - | - | - |
| cron | [ string](#string) | Cron expression defining when ongoing alerts should be sent. Must be a valid cron expression (e.g., "0 9 \* \* MON" for every Monday at 9 AM). |
### OngoingAlertsStrategy.Stream
(synq.alerts.v2.OngoingAlertsStrategy.Stream)
Stream strategy - alerts are sent continuously as issues persist.
No configuration needed - alerts stream continuously.
### Owner
(synq.alerts.v2.Owner)
Owner attributes an alert to an owner and ownership. Only entity-scoped alerts
(issue and schema change) can be owned.
| Field | Type | Description |
| - | - | - |
| owner\_path | [ string](#string) | Path identifying the owner of this alert config. |
| ownership\_id | [ string](#string) | Ownership identifier. Empty for an alert scoped to the owner as a whole rather than to one of its ownerships. |
### SchemaChangeAlert
(synq.alerts.v2.SchemaChangeAlert)
SchemaChangeAlert fires on the schema changes of the entities its trigger
selects.
| Field | Type | Description |
| - | - | - |
| trigger | [ Trigger](#trigger) | Selects the entities that can trigger this alert. |
| targets | [repeated AlertingTarget](#alertingtarget) | Targets where alerts will be sent. At least one is required. |
| settings | [ SchemaChangeAlertSettings](#schemachangealertsettings) | Schema change alert settings. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_owner.owner | [optional Owner](#owner) | Optional owner information. The owner is fixed when the alert is created. |
### SchemaChangeAlertSettings
(synq.alerts.v2.SchemaChangeAlertSettings)
SchemaChangeAlertSettings configures alerts for schema changes.
This alert triggers when the schema of monitored entities changes.
| Field | Type | Description |
| - | - | - |
| notify\_upstream | [ bool](#bool) | Whether to notify upstream dependencies when schema changes occur. |
| ongoing | [ OngoingAlertsStrategy](#ongoingalertsstrategy) | Delivery strategy. Unset or stream sends one alert per schema change; a schedule batches them into a periodic digest on the given cron cadence. |
### Trigger
(synq.alerts.v2.Trigger)
Trigger selects the entities whose issues or schema changes fire this alert:
a structured `query`, an optional ResolverQL string that takes precedence when
set, and a read-only canonical rendering.
| Field | Type | Description |
| - | - | - |
| query | [ Query](#query) | Structured selection of the entities that can trigger this alert. On write it is the selection unless `resolver_ql` is set, which takes precedence. Always populated on read, best-effort — a part with no public form is carried as `unsupported`. |
| resolver\_ql | [ string](#string) | ResolverQL selection string, a more expressive alternative to `query` for selections it cannot express (e.g. lineage walks, column predicates). When set on write it takes precedence and `query` is ignored. On read it is echoed back verbatim for alerts authored via ResolverQL and empty for structured alerts, so writing a read response back unchanged preserves the authored form. See `rendered_resolver_ql` for the ResolverQL form of a structured alert. |
| rendered\_resolver\_ql | [ string](#string) | Output-only canonical ResolverQL rendering of this alert's selection, populated regardless of how it was authored (it equals `resolver_ql` for ResolverQL-authored alerts). Ignored on write. Empty only for selections with no ResolverQL representation. |
### AlertIdentifier
(synq.alerts.services.v2.AlertIdentifier)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) identifier.id | [ string](#string) | ID of the alert configuration to update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) identifier.fqn | [ string](#string) | FQN of the alert configuration to update. |
### BatchGetRequest
(synq.alerts.services.v2.BatchGetRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated AlertIdentifier](#alertidentifier) | IDs of the alerts to get. |
### BatchGetResponse
(synq.alerts.services.v2.BatchGetResponse)
| Field | Type | Description |
| - | - | - |
| alerts | [map AlertsEntry](#alertsentry) | Alerts mapped by their identifiers. |
### BatchGetResponse.AlertsEntry
(synq.alerts.services.v2.BatchGetResponse.AlertsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Alert](#alert) | none |
### ConfigsFilter
(synq.alerts.services.v2.ConfigsFilter)
ConfigsFilter selects alerts by the config that manages them.
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Config IDs to match. An empty string matches the config a deploy without a namespace uses. |
### CreateRequest
(synq.alerts.services.v2.CreateRequest)
CreateRequest creates a new alert configuration. The `kind` oneof carries the
full specification of the alert; its target and trigger constraints are
enforced on the concrete kind messages.
Incident-owner alerts are not created here — turn them on with
ToggleIncidentOwnerAlert.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human-readable name for the alert configuration. |
| fqn | [ string](#string) | User-provided fully qualified name for the alert config. This is a unique identifier that users can specify to reference the alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_template.template | [optional AlertTemplate](#alerttemplate) | Optional custom template for the rendered alert message. Common to every alert kind; currently applied only to issue alerts. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.issue\_lifecycle | [ IssueLifecycleAlert](#issuelifecyclealert) | An issue alert on selected entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.schema\_change | [ SchemaChangeAlert](#schemachangealert) | A schema change alert on selected entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.incident\_lifecycle | [ IncidentLifecycleAlert](#incidentlifecyclealert) | An incident lifecycle alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID that owns this alert when deployed from configuration as code (see `Alert.config_id`); omit it for an alert authored directly. Cannot be combined with `owner.ownership_id`. |
### CreateResponse
(synq.alerts.services.v2.CreateResponse)
CreateResponse returns the created alert configuration.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was created. |
### DeleteRequest
(synq.alerts.services.v2.DeleteRequest)
DeleteRequest specifies which alert configuration to delete.
The config can be identified by either ID or FQN.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to delete. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the delete fails with a conflict (ABORTED) if the alert was modified since this etag was read. |
### DeleteResponse
(synq.alerts.services.v2.DeleteResponse)
DeleteResponse confirms the deletion (empty response).
Empty response - successful deletion is indicated by HTTP 200 status.
### GetIncidentOwnerAlertRequest
(synq.alerts.services.v2.GetIncidentOwnerAlertRequest)
GetIncidentOwnerAlertRequest reads the workspace's incident-owner alert state.
### GetIncidentOwnerAlertResponse
(synq.alerts.services.v2.GetIncidentOwnerAlertResponse)
GetIncidentOwnerAlertResponse reports the current state.
| Field | Type | Description |
| - | - | - |
| is\_enabled | [ bool](#bool) | Whether the incident-owner alert is on. |
### ListRequest
(synq.alerts.services.v2.ListRequest)
ListRequest filters alert configurations to retrieve.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_owner.owner | [optional Owner](#owner) | Optional filter by owner of the alert configurations. Only issue and schema change alerts carry an owner; filtering by owner therefore returns those only. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_configs\_filter.configs\_filter | [optional ConfigsFilter](#configsfilter) | Optional filter by the config the alerts were deployed from. Unset: no config filtering. Empty `ids`: only alerts managed by some config. Non-empty `ids`: only alerts managed by one of those configs. |
### ListResponse
(synq.alerts.services.v2.ListResponse)
ListResponse returns the filtered list of alert configurations.
| Field | Type | Description |
| - | - | - |
| alerts\_ids | [repeated string](#string) | The list of alert configurations matching the filter criteria. |
### RenameRequest
(synq.alerts.services.v2.RenameRequest)
RenameRequest changes an alert's human-readable name.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to rename. |
| name | [ string](#string) | New human-readable name for the alert configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict (ABORTED) if the alert was modified since this etag was read. |
### RenameResponse
(synq.alerts.services.v2.RenameResponse)
RenameResponse returns the renamed alert configuration.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was renamed. |
### SendTestAlertRequest
(synq.alerts.services.v2.SendTestAlertRequest)
SendTestAlertRequest delivers a static test message to the given targets.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Alert name shown in the test message. Optional — leave empty while an alert is still being created; the message then omits the name. |
| kind | [ TestAlertKind](#testalertkind) | The alert kind the test stands in for. Optional; selects the message wording only, so an unspecified kind produces a generic test message. |
| targets | [repeated AlertingTarget](#alertingtarget) | The targets to deliver the test message to. At least one and at most 20 per request. Owner targets are expanded to each owner's configured channels. |
### SendTestAlertResponse
(synq.alerts.services.v2.SendTestAlertResponse)
SendTestAlertResponse reports the per-target outcome of the test send.
| Field | Type | Description |
| - | - | - |
| dispatches | [repeated TestAlertDispatch](#testalertdispatch) | One entry per delivered target, in the order they were resolved. Owner targets contribute one entry per resolved channel. |
### TestAlertDispatch
(synq.alerts.services.v2.TestAlertDispatch)
TestAlertDispatch is the outcome of delivering the test message to one target.
| Field | Type | Description |
| - | - | - |
| target | [ AlertingTarget](#alertingtarget) | The resolved target this outcome refers to. |
| dispatched | [ bool](#bool) | True once the message was handed to the delivery channel; the delivery itself is asynchronous, so this does not confirm the recipient received it. |
| error | [ string](#string) | Why this target was not dispatched; empty when dispatched is true. |
### ToggleEnabledRequest
(synq.alerts.services.v2.ToggleEnabledRequest)
ToggleEnabledRequest enables or disables an alert configuration.
The config can be identified by either ID or FQN.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to toggle. |
| is\_enabled | [ bool](#bool) | Whether the alert configuration should be enabled. true = enable the alert, false = disable the alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict (ABORTED) if the alert was modified since this etag was read. |
### ToggleEnabledResponse
(synq.alerts.services.v2.ToggleEnabledResponse)
ToggleEnabledResponse confirms the toggle operation (empty response).
Empty response - successful toggle is indicated by HTTP 200 status.
### ToggleIncidentOwnerAlertRequest
(synq.alerts.services.v2.ToggleIncidentOwnerAlertRequest)
ToggleIncidentOwnerAlertRequest turns the workspace's incident-owner alert on
or off.
| Field | Type | Description |
| - | - | - |
| is\_enabled | [ bool](#bool) | true turns the incident-owner alert on (creating it if absent); false turns it off (removing it). Setting the current state is a no-op. |
### ToggleIncidentOwnerAlertResponse
(synq.alerts.services.v2.ToggleIncidentOwnerAlertResponse)
ToggleIncidentOwnerAlertResponse reports the resulting state.
| Field | Type | Description |
| - | - | - |
| is\_enabled | [ bool](#bool) | Whether the incident-owner alert is on after the call. |
### UpdateSettingsRequest
(synq.alerts.services.v2.UpdateSettingsRequest)
UpdateSettingsRequest updates an alert's trigger and/or its kind-specific
settings. At least one of the two must be provided; whichever is omitted is
left unchanged (neither can be cleared). The provided settings must match the
alert's existing kind — switching an alert from one kind to another is not
permitted.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_trigger.trigger | [optional Trigger](#trigger) | New entity selection. Issue and schema change alerts only. Omit to keep the current trigger; it cannot be cleared. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) settings.issue\_lifecycle | [ IssueAlertSettings](#issuealertsettings) | Settings for an issue alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) settings.schema\_change | [ SchemaChangeAlertSettings](#schemachangealertsettings) | Settings for a schema change alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) settings.incident\_lifecycle | [ IncidentLifecycleAlertSettings](#incidentlifecyclealertsettings) | Settings for an incident lifecycle alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict (ABORTED) if the alert was modified since this etag was read. |
### UpdateSettingsResponse
(synq.alerts.services.v2.UpdateSettingsResponse)
UpdateSettingsResponse returns the updated alert configuration.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was updated. |
### UpdateTargetsRequest
(synq.alerts.services.v2.UpdateTargetsRequest)
UpdateTargetsRequest replaces an alert's delivery targets.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to update. |
| targets | [repeated AlertingTarget](#alertingtarget) | The complete set of delivery targets, replacing the existing ones. At least one target is required; the allowed target types depend on the alert's kind. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict (ABORTED) if the alert was modified since this etag was read. |
### UpdateTargetsResponse
(synq.alerts.services.v2.UpdateTargetsResponse)
UpdateTargetsResponse returns the updated alert configuration.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was updated. |
### UpdateTemplateRequest
(synq.alerts.services.v2.UpdateTemplateRequest)
UpdateTemplateRequest sets or clears an alert's message template.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_template.template | [optional AlertTemplate](#alerttemplate) | New custom template for the rendered alert message. Omit to clear the template and revert to the default rendering. Currently applied only to issue alerts; accepted and ignored for other kinds. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict (ABORTED) if the alert was modified since this etag was read. |
### UpdateTemplateResponse
(synq.alerts.services.v2.UpdateTemplateResponse)
UpdateTemplateResponse returns the updated alert configuration.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was updated. |
### UpsertRequest
(synq.alerts.services.v2.UpsertRequest)
UpsertRequest states the complete alert held under `fqn`. Nothing is merged
with the stored alert: every field is written as sent, so an omitted
`template` clears it and the targets replace the stored ones. The only
exception is `is_disabled`, described on the field.
The alert kind, the `owner` inside it, and `config_id` are fixed when an alert
is created. When an alert already holds `fqn` and any of them differs from the
stored value, the request fails with FAILED\_PRECONDITION and nothing is
written; to change one, delete the alert and upsert it again.
The trigger is stored as sent, as with Create. A `query` read back from the
API carries parts it cannot express as `unsupported`, so to write a stored
selection back unchanged send the `rendered_resolver_ql` you read as
`resolver_ql`.
Incident-owner alerts cannot be upserted — manage them with
ToggleIncidentOwnerAlert.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human-readable name for the alert configuration. |
| fqn | [ string](#string) | Fully qualified name identifying the alert. An alert holding it is replaced; otherwise a new alert is created with it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_template.template | [optional AlertTemplate](#alerttemplate) | Custom template for the rendered alert message. Omit to use the default rendering, clearing any stored template. Currently applied only to issue alerts; accepted and ignored for other kinds. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.issue\_lifecycle | [ IssueLifecycleAlert](#issuelifecyclealert) | An issue alert on selected entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.schema\_change | [ SchemaChangeAlert](#schemachangealert) | A schema change alert on selected entities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.incident\_lifecycle | [ IncidentLifecycleAlert](#incidentlifecyclealert) | An incident lifecycle alert. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID that owns this alert when deployed from configuration as code (see `Alert.config_id`); omit it for an alert authored directly. Cannot be combined with `owner.ownership_id`. For an existing alert it must equal the stored value, where omitted and the empty string are different values. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_disabled.is\_disabled | [optional bool](#bool) | Whether the alert is paused. When omitted, a new alert is created enabled and an existing alert keeps its current state, so a deploy that does not manage pausing never re-enables an alert someone paused in the app. Set it to make the deploy own the paused state. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard: the write fails with ABORTED if the alert holding `fqn` was modified since this etag was read, or no longer exists. |
### UpsertResponse
(synq.alerts.services.v2.UpsertResponse)
UpsertResponse returns the stored alert after the write.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration as stored. |
| created | [ bool](#bool) | True when no alert held `fqn` and this call created one. |
### AlertIdentifier
(synq.alerts.services.v1.AlertIdentifier)
Deprecated: use synq.alerts.services.v2.AlertIdentifier.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) identifier.id | [ string](#string) | ID of the alert configuration to update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) identifier.fqn | [ string](#string) | FQN of the alert configuration to update. |
### BatchGetRequest
(synq.alerts.services.v1.BatchGetRequest)
Deprecated: use synq.alerts.services.v2.BatchGetRequest.
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated AlertIdentifier](#alertidentifier) | IDs of the alerts to get. |
### BatchGetResponse
(synq.alerts.services.v1.BatchGetResponse)
Deprecated: use synq.alerts.services.v2.BatchGetResponse.
| Field | Type | Description |
| - | - | - |
| alerts | [map AlertsEntry](#alertsentry) | Alerts mapped by their identifiers. |
### BatchGetResponse.AlertsEntry
(synq.alerts.services.v1.BatchGetResponse.AlertsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Alert](#alert) | none |
### CreateRequest
(synq.alerts.services.v1.CreateRequest)
CreateRequest creates a new alert configuration.
Deprecated: use synq.alerts.services.v2.CreateRequest.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human-readable name for the alert configuration. |
| fqn | [ string](#string) | User-provided fully qualified name for the alert config. This is a unique identifier that users can specify to reference the alert. |
| trigger | [ EntityGroupQuery](#entitygroupquery) | Query that defines which entities can trigger this alert. |
| targets | [repeated AlertingTarget](#alertingtarget) | Targets where alerts will be sent. |
| settings | [ AlertSettings](#alertsettings) | Additional settings for the specific alert type. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_owner.owner | [optional Owner](#owner) | Optional owner information for the alert configuration. |
### CreateResponse
(synq.alerts.services.v1.CreateResponse)
CreateResponse returns the created alert configuration.
Deprecated: use synq.alerts.services.v2.CreateResponse.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was created. |
### DeleteRequest
(synq.alerts.services.v1.DeleteRequest)
DeleteRequest specifies which alert configuration to delete.
The config can be identified by either ID or FQN.
Deprecated: use synq.alerts.services.v2.DeleteRequest.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to delete. |
### DeleteResponse
(synq.alerts.services.v1.DeleteResponse)
DeleteResponse confirms the deletion (empty response).
Deprecated: use synq.alerts.services.v2.DeleteResponse.
### ListRequest
(synq.alerts.services.v1.ListRequest)
ListRequest filters alert configurations to retrieve.
Deprecated: use synq.alerts.services.v2.ListRequest.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_owner.owner | [optional Owner](#owner) | Optional filter by owner of the alert configurations. |
### ListResponse
(synq.alerts.services.v1.ListResponse)
ListResponse returns the filtered list of alert configurations.
Deprecated: use synq.alerts.services.v2.ListResponse.
| Field | Type | Description |
| - | - | - |
| alerts\_ids | [repeated string](#string) | The list of alert configurations matching the filter criteria. |
### ToggleEnabledRequest
(synq.alerts.services.v1.ToggleEnabledRequest)
ToggleEnabledRequest enables or disables an alert configuration.
The config can be identified by either ID or FQN.
Deprecated: use synq.alerts.services.v2.ToggleEnabledRequest.
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to toggle. |
| is\_enabled | [ bool](#bool) | Whether the alert configuration should be enabled. true = enable the alert, false = disable the alert. |
### ToggleEnabledResponse
(synq.alerts.services.v1.ToggleEnabledResponse)
ToggleEnabledResponse confirms the toggle operation (empty response).
Deprecated: use synq.alerts.services.v2.ToggleEnabledResponse.
### UpdateRequest
(synq.alerts.services.v1.UpdateRequest)
UpdateRequest updates an existing alert configuration.
The config can be identified by either ID or FQN.
Deprecated: use the intent-specific requests on synq.alerts.services.v2
(RenameRequest, UpdateTargetsRequest, UpdateSettingsRequest, UpdateTemplateRequest).
| Field | Type | Description |
| - | - | - |
| identifier | [ AlertIdentifier](#alertidentifier) | Identifier for the config to update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_name.name | [optional string](#string) | Human-readable name for the alert configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_trigger.trigger | [optional EntityGroupQuery](#entitygroupquery) | Query that defines which entities can trigger this alert. |
| targets | [repeated AlertingTarget](#alertingtarget) | Targets where alerts will be sent. Leave empty to keep existing targets. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_settings.settings | [optional AlertSettings](#alertsettings) | Additional settings for the specific alert type. |
### UpdateResponse
(synq.alerts.services.v1.UpdateResponse)
UpdateResponse returns the updated alert configuration.
Deprecated: use the intent-specific responses on synq.alerts.services.v2.
| Field | Type | Description |
| - | - | - |
| alert | [ Alert](#alert) | The alert configuration that was updated. |
### AlertingTarget
(synq.alerts.v1.AlertingTarget)
AlertingTarget represents a destination where alert notifications will be sent.
Each target type has its own specific configuration requirements.
Deprecated: use synq.alerts.v2.AlertingTarget.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.slack | [ SlackTarget](#slacktarget) | Send alert to a Slack channel or user. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.email | [ EmailTarget](#emailtarget) | Send alert via email. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.owner | [ OwnerTarget](#ownertarget) | Send alert to the owner of the monitored entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.ms\_teams | [ MsTeamsTarget](#msteamstarget) | Send alert to Microsoft Teams channel or member. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) target.webhook | [ WebhookTarget](#webhooktarget) | Send alert via webhook integration. |
### EmailTarget
(synq.alerts.v1.EmailTarget)
EmailTarget configures alert delivery via email.
Deprecated: use synq.alerts.v2.EmailTarget.
| Field | Type | Description |
| - | - | - |
| recipient\_emails | [repeated string](#string) | List of email addresses that will receive the alert notifications. Each email must be a valid email address format. |
### MsTeamsTarget
(synq.alerts.v1.MsTeamsTarget)
MsTeamsTarget configures alert delivery to Microsoft Teams.
Alerts are sent to either a Microsoft Teams channel or a specific member.
Deprecated: use synq.alerts.v2.MsTeamsTarget.
| Field | Type | Description |
| - | - | - |
| channel | [ string](#string) | Teams channel name or ID. Leave unset to direct-message a member instead. |
| member\_id | [ string](#string) | Microsoft Teams member id for direct-message delivery. |
### OwnerTarget
(synq.alerts.v1.OwnerTarget)
OwnerTarget configures alert delivery to the owner of the monitored entity.
This allows alerts to be automatically sent to whoever owns the data being monitored.
Deprecated: use synq.alerts.v2.OwnerTarget.
| Field | Type | Description |
| - | - | - |
| owner\_path | [ string](#string) | Path that identifies the ownership context for this target. This determines how the system will resolve the actual owner. |
### SlackTarget
(synq.alerts.v1.SlackTarget)
SlackTarget configures alert delivery to Slack.
Alerts are sent to a Slack channel.
Deprecated: use synq.alerts.v2.SlackTarget.
| Field | Type | Description |
| - | - | - |
| channel | [ string](#string) | Slack channel name or ID (e.g., "#alerts" or "C1234567890"). |
### WebhookTarget
(synq.alerts.v1.WebhookTarget)
WebhookTarget configures alert delivery via webhook/hook integrations.
This allows integration with external systems and custom notification workflows.
Deprecated: use synq.alerts.v2.WebhookTarget.
| Field | Type | Description |
| - | - | - |
| webhook\_id | [ string](#string) | Unique identifier of the webhook to send alerts to. This references a pre-configured webhook in the system. |
### Alert
(synq.alerts.v1.Alert)
Alert represents the configuration for an alert.
It contains all the necessary information to trigger and send alerts.
Deprecated: use synq.alerts.v2.Alert, whose trigger is the canonical
synq.queries.v1.Query + resolver\_ql selection. This message is served only by
the deprecated synq.alerts.services.v1.AlertsService and is scheduled for removal.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Unique identifier for the alert config (system-generated). |
| name | [ string](#string) | Human-readable name for the alert configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fqn.fqn | [optional string](#string) | User-provided fully qualified name for the alert config. This is a unique identifier that users can specify to reference the alert. |
| trigger | [ EntityGroupQuery](#entitygroupquery) | Query that defines which entities can trigger this alert. |
| targets | [repeated AlertingTarget](#alertingtarget) | List of targets where alerts will be sent. |
| settings | [ AlertSettings](#alertsettings) | Additional settings for the specific alert type. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_owner.owner | [optional Owner](#owner) | none |
| created\_at | [ Timestamp](#timestamp) | Timestamp when the alert config was created. |
| is\_disabled | [ bool](#bool) | Whether the alert config is currently disabled. |
### Alert.Owner
(synq.alerts.v1.Alert.Owner)
Owner information for the alert configuration.
If set, the alert is attributed to the specified owner and ownership.
Deprecated: use synq.alerts.v2.Owner.
| Field | Type | Description |
| - | - | - |
| owner\_path | [ string](#string) | Path identifying the owner of this alert config. |
| ownership\_id | [ string](#string) | Ownership identifier. |
### AlertSettings
(synq.alerts.v1.AlertSettings)
AlertSettings defines the properties that affect the trigger and presentation of an alert.
It specifies the conditions under which an alert should be fired and how it should behave.
Deprecated: synq.alerts.v2.Alert carries its settings per alert type
(synq.alerts.v2.IssueLifecycleAlert, synq.alerts.v2.SchemaChangeAlert).
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) settings.schema\_change | [ SchemaChangeAlertSettings](#schemachangealertsettings) | Alert triggered by schema changes. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) settings.issue | [ IssueAlertSettings](#issuealertsettings) | Unified issue alert settings (replaces entity\_failure and issue\_lifecycle). |
### EntityGroupQuery
(synq.alerts.v1.EntityGroupQuery)
EntityGroupQuery defines a query to select a group of entities.
This is used to specify which entities should be monitored by an alert.
Deprecated: the v2 trigger selects entities with synq.queries.v1.Query +
resolver\_ql instead. Scheduled for removal with synq.alerts.services.v1.
| Field | Type | Description |
| - | - | - |
| parts | [repeated SelectionQuery](#selectionquery) | The parts that compose this entity group query. |
### IssueAlertSettings
(synq.alerts.v1.IssueAlertSettings)
IssueAlertSettings is the unified alert settings type for issue-based alerts.
It replaces both EntityFailureAlertSettings and IssueLifecycleAlertSettings.
Deprecated: use synq.alerts.v2.IssueAlertSettings.
| Field | Type | Description |
| - | - | - |
| severities | [repeated Severity](#severity) | The severity levels that should trigger this alert. |
| notify\_upstream | [ bool](#bool) | Whether to notify upstream dependencies when this alert triggers. |
| allow\_sql\_test\_audit\_link | [ bool](#bool) | Whether to allow SQL test audit links in alert notifications. |
| ongoing | [ OngoingAlertsStrategy](#ongoingalertsstrategy) | Strategy for handling ongoing/repeated alerts. |
| grouping | [ IssueGroupingStrategy](#issuegroupingstrategy) | Strategy for grouping issues in alert notifications. |
### IssueGroupingStrategy
(synq.alerts.v1.IssueGroupingStrategy)
IssueGroupingStrategy controls how issues are grouped for alerting purposes.
Deprecated: use synq.alerts.v2.IssueGroupingStrategy.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.no\_grouping | [ NoGrouping](#nogrouping) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.system\_detected | [ SystemDetected](#systemdetected) | none |
### IssueGroupingStrategy.NoGrouping
(synq.alerts.v1.IssueGroupingStrategy.NoGrouping)
NoGrouping - each issue triggers a separate alert.
### IssueGroupingStrategy.SystemDetected
(synq.alerts.v1.IssueGroupingStrategy.SystemDetected)
SystemDetected - the system automatically groups related issues.
### OngoingAlertsStrategy
(synq.alerts.v1.OngoingAlertsStrategy)
OngoingAlertsStrategy defines how to handle ongoing/repeated alerts.
This prevents alert spam by controlling notification frequency.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.disabled | [ Disabled](#disabled) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.stream | [ Stream](#stream) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) strategy.schedule | [ Schedule](#schedule) | none |
### OngoingAlertsStrategy.Disabled
(synq.alerts.v1.OngoingAlertsStrategy.Disabled)
Disabled strategy - no ongoing alerts will be sent after the initial alert.
No configuration needed - alerts are simply disabled after the first one.
### OngoingAlertsStrategy.Schedule
(synq.alerts.v1.OngoingAlertsStrategy.Schedule)
Schedule strategy - alerts are sent on a scheduled basis.
| Field | Type | Description |
| - | - | - |
| cron | [ string](#string) | Cron expression defining when ongoing alerts should be sent. Must be a valid cron expression (e.g., "0 9 \* \* MON" for every Monday at 9 AM). |
### OngoingAlertsStrategy.Stream
(synq.alerts.v1.OngoingAlertsStrategy.Stream)
Stream strategy - alerts are sent continuously as issues persist.
No configuration needed - alerts stream continuously.
### SchemaChangeAlertSettings
(synq.alerts.v1.SchemaChangeAlertSettings)
SchemaChangeAlertSettings configures alerts for schema changes.
This alert triggers when the schema of monitored entities changes.
Deprecated: use synq.alerts.v2.SchemaChangeAlertSettings.
| Field | Type | Description |
| - | - | - |
| notify\_upstream | [ bool](#bool) | Whether to notify upstream dependencies when schema changes occur. |
| ongoing | [ OngoingAlertsStrategy](#ongoingalertsstrategy) | Delivery strategy. Unset or stream sends one alert per schema change; a schedule batches them into a periodic digest on the given cron cadence. |
### SelectionQuery
(synq.alerts.v1.SelectionQuery)
SelectionQuery represents a query that selects entities based on various criteria.
It combines multiple query parts with a logical operand.
Deprecated: the v2 trigger selects entities with synq.queries.v1.Query +
resolver\_ql instead. Scheduled for removal with synq.alerts.services.v1.
| Field | Type | Description |
| - | - | - |
| parts | [repeated QueryPart](#querypart) | The individual query parts that make up this selection. |
| operand | [ QueryOperand](#queryoperand) | The logical operand used to combine the query parts (AND, OR, etc.). |
### SelectionQuery.QueryPart
(synq.alerts.v1.SelectionQuery.QueryPart)
A single part of a selection query.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier\_list | [ IdentifierList](#identifierlist) | Filter by specific entity identifiers. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_name\_search | [ WithNameSearch](#withnamesearch) | Filter by name pattern search. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_type | [ WithType](#withtype) | Filter by entity type. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_annotation | [ WithAnnotation](#withannotation) | Filter by annotation presence/value. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_data\_platform | [ InDataPlatform](#indataplatform) | Filter by data platform. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_folder | [ InFolder](#infolder) | Filter by folder location. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_data\_platform\_type | [ WithDataPlatformType](#withdataplatformtype) | Filter by data platform type. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_dataproduct | [ InDataproduct](#indataproduct) | Filter by data product membership. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.entity\_ids | [ EntityIds](#entityids) | Alert on specific entities, named by the ids the API returned for them. Read back as `identifier_list`, with each id on the identifier it names. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ SelectionQuery](#selectionquery) | Nested selection query for complex filtering. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.unsupported | [ Unsupported](#unsupported) | Unsupported query types for backward compatibility. |
### AlertTemplate
(synq.alerts.templates.v2.AlertTemplate)
AlertTemplate customises the message rendered for an alert.
The body is either verbatim text (`plain`), rendered exactly as given, or a
structured template (`structured`) assembled from named blocks written in
Jinja-compatible syntax and grouped by where they render.
| Field | Type | Description |
| - | - | - |
| title | [ string](#string) | Optional custom title for the rendered message. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) body.plain | [ string](#string) | Verbatim message text, rendered as-is with no templating. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) body.structured | [ StructuredTemplate](#structuredtemplate) | A structured, dynamically rendered body. |
### StructuredTemplate
(synq.alerts.templates.v2.StructuredTemplate)
StructuredTemplate is a dynamic template split into the two tiers a message
renders in. Within each tier, blocks render in list order.
A block that should appear in both tiers is listed in both `summary` and
`issue_details` with the same `name`.
| Field | Type | Description |
| - | - | - |
| summary | [repeated TemplateBlock](#templateblock) | Blocks rendered once, in the group/summary message. |
| issue\_details | [repeated TemplateBlock](#templateblock) | Blocks rendered per issue, in the per-issue attachment or card. |
### TemplateBlock
(synq.alerts.templates.v2.TemplateBlock)
TemplateBlock is one unit of Jinja-compatible source.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_name.name | [optional string](#string) | Optional block name. A block listed in both `summary` and `issue_details` under the same name is one shared block. Leave it unset and the server assigns and returns a stable name. Names reserved for built-in catalog blocks render under the server's own layout rather than emitting your content verbatim, so give a custom block a distinct name. |
| content | [ string](#string) | Jinja-compatible source of the block body. |
### Domain
(synq.domains.v2.Domain)
A data domain: a named area of ownership that groups the assets, data products
and sub-domains belonging to one part of the business.
Domains form a hierarchy through `parent_id`, and a parent's membership
includes everything its sub-domains contain.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the domain (a UUID). Supplied by the caller on create, which makes Upsert idempotent. This is the value to pass wherever another API accepts a domain reference (for example the `in_domain` query operand). |
| name | [ string](#string) | Human-readable name. |
| description | [ string](#string) | Optional longer description. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_id.parent\_id | [optional string](#string) | Id of the parent domain, if this is a sub-domain. Absent for a top-level domain. |
| source | [ Source](#source) | none |
| definition | [ DomainDefinition](#domaindefinition) | The membership definition. Omitted when a read requests `exclude_definition`. |
| etag | [ string](#string) | Entity tag for optimistic concurrency: pass back on Upsert / Delete / definition writes to guard against overwriting a concurrent change. Form: `W/"-"`. Opaque — do not parse. |
| created\_by | [ Actor](#actor) | Who created the domain. Output-only. |
| updated\_by | [ Actor](#actor) | Who last edited the domain. Output-only. |
| created\_at | [ Timestamp](#timestamp) | When the domain was created. Output-only. |
| updated\_at | [ Timestamp](#timestamp) | When the domain was last updated. Output-only. Absent for a domain that has not been written since this field was introduced. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | When the domain was soft-deleted, if it has been. Output-only. |
### BatchGetRequest
(synq.domains.v2.BatchGetRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Ids to fetch (1–200, unique). Each accepts either the bare UUID or the `domain-` form; the prefix is stripped server-side. |
| include\_deleted | [ bool](#bool) | Include soft-deleted domains. |
| exclude\_definition | [ bool](#bool) | Omit the (potentially large) membership definition from each domain. |
### BatchGetResponse
(synq.domains.v2.BatchGetResponse)
| Field | Type | Description |
| - | - | - |
| domains | [map DomainsEntry](#domainsentry) | Domains keyed by id. Ids that do not exist (or are deleted, unless requested) are absent. |
### BatchGetResponse.DomainsEntry
(synq.domains.v2.BatchGetResponse.DomainsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Domain](#domain) | none |
### DeleteRequest
(synq.domains.v2.DeleteRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the domain to delete. Accepts the bare UUID or the `domain-` form (the prefix is stripped server-side). |
| purge | [ bool](#bool) | Hard-delete instead of soft-delete, releasing the id for reuse. Refused while the domain still has sub-domains — delete or re-parent them first. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
### DeleteResponse
(synq.domains.v2.DeleteResponse)
### FetchDomainWithSubDomainsRequest
(synq.domains.v2.FetchDomainWithSubDomainsRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the domain. Accepts the bare UUID or the `domain-` form. |
| exclude\_definition | [ bool](#bool) | Omit the membership definition from each domain. |
### FetchDomainWithSubDomainsResponse
(synq.domains.v2.FetchDomainWithSubDomainsResponse)
| Field | Type | Description |
| - | - | - |
| domains | [map DomainsEntry](#domainsentry) | The domain and its direct sub-domains, keyed by id. |
### FetchDomainWithSubDomainsResponse.DomainsEntry
(synq.domains.v2.FetchDomainWithSubDomainsResponse.DomainsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Domain](#domain) | none |
### GetUsageRequest
(synq.domains.v2.GetUsageRequest)
### GetUsageResponse
(synq.domains.v2.GetUsageResponse)
| Field | Type | Description |
| - | - | - |
| num\_domains | [ int32](#int32) | Number of top-level domains. |
| num\_sub\_domains | [ int32](#int32) | Number of sub-domains, at any depth. |
### ListMembersRequest
(synq.domains.v2.ListMembersRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the domain whose members to list. Accepts the bare UUID or the `domain-` form (the prefix is stripped server-side). |
| pagination | [ Pagination](#pagination) | Pagination. |
### ListMembersResponse
(synq.domains.v2.ListMembersResponse)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Opaque entity ids of the assets that currently belong to the domain. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### ListRequest
(synq.domains.v2.ListRequest)
| Field | Type | Description |
| - | - | - |
| pagination | [ Pagination](#pagination) | Pagination. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_id.parent\_id | [optional string](#string) | Restrict to the children of one domain. Pass an empty string to return only top-level domains. Omit to return every domain at any level. |
| include\_deleted | [ bool](#bool) | Include soft-deleted domains. |
| exclude\_definition | [ bool](#bool) | Omit the membership definition from each domain. |
### ListResponse
(synq.domains.v2.ListResponse)
| Field | Type | Description |
| - | - | - |
| domains | [repeated Domain](#domain) | The matching domains. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### RemoveDefinitionPartRequest
(synq.domains.v2.RemoveDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the domain. Accepts the bare UUID or the `domain-` form (the prefix is stripped server-side). |
| part\_id | [ string](#string) | Id of the part to remove. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### RemoveDefinitionPartResponse
(synq.domains.v2.RemoveDefinitionPartResponse)
| Field | Type | Description |
| - | - | - |
| domain | [ Domain](#domain) | The stored domain after the part removal. |
### SetDefinitionRequest
(synq.domains.v2.SetDefinitionRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the domain whose definition to replace. Accepts the bare UUID or the `domain-` form (the prefix is stripped server-side). |
| definition | [ DomainDefinition](#domaindefinition) | The new membership definition (replaces the existing one entirely). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### SetDefinitionResponse
(synq.domains.v2.SetDefinitionResponse)
| Field | Type | Description |
| - | - | - |
| domain | [ Domain](#domain) | The stored domain after the definition write. |
### UpsertDefinitionPartRequest
(synq.domains.v2.UpsertDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the domain. Accepts the bare UUID or the `domain-` form (the prefix is stripped server-side). |
| part | [ Part](#part) | The part to add or replace (matched by its part id). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### UpsertDefinitionPartResponse
(synq.domains.v2.UpsertDefinitionPartResponse)
| Field | Type | Description |
| - | - | - |
| domain | [ Domain](#domain) | The stored domain after the part write. |
### UpsertRequest
(synq.domains.v2.UpsertRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the domain. Accepts either the bare UUID or the `domain-` form — the prefix is stripped server-side, so the value you read back from any API can be passed straight in. The same id updates the same domain; a new id creates a new domain. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_name.name | [optional string](#string) | Name. Required when creating; omit to keep the existing name on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Description. Omit to keep the existing description. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_id.parent\_id | [optional string](#string) | Parent domain, making this a sub-domain. Omit to keep the current parent; pass an empty string to detach the domain and make it top-level. Accepts the bare UUID or the `domain-` form. The parent must already exist, and the write is rejected if it would make the hierarchy loop. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source.source | [optional Source](#source) | Source. Omit to keep the existing source (defaults to SOURCE\_SYNQ on create). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_definition.definition | [optional DomainDefinition](#domaindefinition) | Membership definition. Omit to keep the existing definition; pass a present (possibly empty) definition to replace it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the update fails with a conflict if the domain was modified since this etag was read. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### UpsertResponse
(synq.domains.v2.UpsertResponse)
| Field | Type | Description |
| - | - | - |
| domain | [ Domain](#domain) | The stored domain after the upsert. |
### DomainDefinition
(synq.domains.v2.DomainDefinition)
The membership of a domain: which assets belong to it. A definition is an
ordered list of parts; an asset is a member if it matches ANY part (the parts
are OR'd together).
Unlike a data product, a domain is NOT a leaf: a part may pull in a whole data
product or another domain, and membership is then resolved recursively. The
server maintains the reference graph across domains and data products and
rejects any write that would close a loop, naming the offending path — so a
definition can always be resolved in finite time. The domain hierarchy counts
as a reference too: a parent domain aggregates its sub-domains.
| Field | Type | Description |
| - | - | - |
| parts | [repeated Part](#part) | The parts of the definition. An asset is a member if it matches any part. |
### DomainDefinition.Part
(synq.domains.v2.DomainDefinition.Part)
A single piece of a domain definition: a pinned asset, a whole data product,
a whole other domain, or a selection query.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Stable id of this part (a UUID). Supplied by the caller for idempotent part upserts; the server mints one if left empty on create. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.entity\_id | [ string](#string) | Pin a single asset by its opaque entity id (as returned by resolve / listing APIs). Never constructed by hand. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.dataproduct\_id | [ string](#string) | Include everything that belongs to a data product. Accepts either the bare data-product UUID or the `dataproduct-` form that API returns as its `entity_id` — the prefix is stripped server-side. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.domain\_id | [ string](#string) | Include everything that belongs to another domain, including that domain's own sub-domains. Accepts either the bare domain UUID or the `domain-` form — the prefix is stripped server-side. Rejected when it would make the domains reference each other in a loop. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ DomainQuery](#domainquery) | Select assets by a query. |
A query whose whole content is a single domain reference is equivalent to `domain_id` and is returned in that form on read, since both describe the same membership. |
### DomainQuery
(synq.domains.v2.DomainQuery)
The selection query behind a domain definition part, expressed for the public
API.
On write, provide the query in ONE of three forms:
* `resolver_ql` — the resolver query language, a compact text form that is
the canonical way to author selections as code;
* `public_query` — a structured query (the same shape used elsewhere for
asset selection), convenient to build from a filter UI; or
* `asset_selection_query` — the structured form domains have always
accepted, kept so an existing definition can be moved across unchanged.
When more than one is set the first of that list wins. The server compiles the
query and stores it canonically; the raw text is not preserved verbatim.
On read, the server returns `rendered_resolver_ql` (the stored query rendered
back to canonical resolver query language) plus, best-effort, both structured
forms. `resolver_ql` is not echoed back — read `rendered_resolver_ql`.
A domain query may reference another domain or a data product (via the
`in_domain` / `in_dataproduct` operands, or their resolver-query-language
equivalents). Such a reference is resolved recursively and is rejected only
when it would close a reference loop.
| Field | Type | Description |
| - | - | - |
| resolver\_ql | [ string](#string) | WRITE (option A). The query in resolver query language. |
| rendered\_resolver\_ql | [ string](#string) | READ, output-only. The stored query rendered back to canonical resolver query language. Deterministically derived; does not preserve original hand-typed formatting or comments. |
| public\_query | [ Query](#query) | WRITE (option B) and READ. On write, the structured query to store (used when `resolver_ql` is empty). On read, a best-effort structured representation of the stored query. |
| asset\_selection\_query | [ AssetSelectionQuery](#assetselectionquery) | WRITE (option C) and READ. The structured form carried over from `synq.domains.v1`, for definitions authored against that API. On write it is used only when neither `resolver_ql` nor `public_query` is set. On read it is the stored query as-is, so a v1 definition round-trips through v2 unchanged. `public_query` is the richer form — prefer it for new integrations, since it can express domain and data-product references this one cannot. |
### DomainMembership
(synq.domains.members.v1.DomainMembership)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
| domain\_id | [ string](#string) | none |
| is\_default | [ bool](#bool) | none |
### AddDomainMembersRequest
(synq.domains.members.v1.AddDomainMembersRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| user\_ids | [repeated string](#string) | none |
### AddDomainMembersResponse
(synq.domains.members.v1.AddDomainMembersResponse)
### ListDomainMembersRequest
(synq.domains.members.v1.ListDomainMembersRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
### ListDomainMembersResponse
(synq.domains.members.v1.ListDomainMembersResponse)
| Field | Type | Description |
| - | - | - |
| memberships | [repeated DomainMembership](#domainmembership) | none |
### ListUserDomainsRequest
(synq.domains.members.v1.ListUserDomainsRequest)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
### ListUserDomainsResponse
(synq.domains.members.v1.ListUserDomainsResponse)
| Field | Type | Description |
| - | - | - |
| memberships | [repeated DomainMembership](#domainmembership) | none |
### RemoveDomainMembersRequest
(synq.domains.members.v1.RemoveDomainMembersRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| user\_ids | [repeated string](#string) | none |
### RemoveDomainMembersResponse
(synq.domains.members.v1.RemoveDomainMembersResponse)
### SetDefaultUserDomainRequest
(synq.domains.members.v1.SetDefaultUserDomainRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| user\_id | [ string](#string) | none |
### SetDefaultUserDomainResponse
(synq.domains.members.v1.SetDefaultUserDomainResponse)
### BatchGetRequest
(synq.domains.v1.BatchGetRequest)
| Field | Type | Description |
| - | - | - |
| domain\_ids | [repeated string](#string) | none |
### BatchGetResponse
(synq.domains.v1.BatchGetResponse)
| Field | Type | Description |
| - | - | - |
| domains | [map DomainsEntry](#domainsentry) | none |
### BatchGetResponse.DomainsEntry
(synq.domains.v1.BatchGetResponse.DomainsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Domain](#domain) | none |
### DeleteRequest
(synq.domains.v1.DeleteRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
### DeleteResponse
(synq.domains.v1.DeleteResponse)
### FetchDomainWithSubDomainsRequest
(synq.domains.v1.FetchDomainWithSubDomainsRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| exclude\_definition | [ bool](#bool) | none |
### FetchDomainWithSubDomainsResponse
(synq.domains.v1.FetchDomainWithSubDomainsResponse)
| Field | Type | Description |
| - | - | - |
| domains | [map DomainsEntry](#domainsentry) | none |
### FetchDomainWithSubDomainsResponse.DomainsEntry
(synq.domains.v1.FetchDomainWithSubDomainsResponse.DomainsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Domain](#domain) | none |
### GetUsageRequest
(synq.domains.v1.GetUsageRequest)
### GetUsageResponse
(synq.domains.v1.GetUsageResponse)
| Field | Type | Description |
| - | - | - |
| num\_domains | [ int32](#int32) | none |
| num\_sub\_domains | [ int32](#int32) | none |
### ListRequest
(synq.domains.v1.ListRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_id.parent\_id | [optional string](#string) | none |
| exclude\_definition | [ bool](#bool) | none |
### ListResponse
(synq.domains.v1.ListResponse)
| Field | Type | Description |
| - | - | - |
| domains | [repeated Domain](#domain) | none |
### RemoveDefinitionPartRequest
(synq.domains.v1.RemoveDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| part\_id | [ string](#string) | none |
### RemoveDefinitionPartResponse
(synq.domains.v1.RemoveDefinitionPartResponse)
### SetDefinitionRequest
(synq.domains.v1.SetDefinitionRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| definition | [ DomainDefinition](#domaindefinition) | none |
### SetDefinitionResponse
(synq.domains.v1.SetDefinitionResponse)
### UpsertDefinitionPartRequest
(synq.domains.v1.UpsertDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
| part | [ Part](#part) | none |
### UpsertDefinitionPartResponse
(synq.domains.v1.UpsertDefinitionPartResponse)
### UpsertRequest
(synq.domains.v1.UpsertRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| name | [ string](#string) | none |
| description | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_id.parent\_id | [optional string](#string) | none |
| source | [ Source](#source) | none |
### UpsertResponse
(synq.domains.v1.UpsertResponse)
### AssetSelectionQuery
(synq.domains.v1.AssetSelectionQuery)
| Field | Type | Description |
| - | - | - |
| parts | [repeated QueryPart](#querypart) | none |
| operand | [ QueryOperand](#queryoperand) | none |
### AssetSelectionQuery.QueryPart
(synq.domains.v1.AssetSelectionQuery.QueryPart)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier\_list | [ IdentifierList](#identifierlist) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_name\_search | [ WithNameSearch](#withnamesearch) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_type | [ WithType](#withtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_annotation | [ WithAnnotation](#withannotation) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_data\_platform | [ InDataPlatform](#indataplatform) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_folder | [ InFolder](#infolder) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_data\_platform\_type | [ WithDataPlatformType](#withdataplatformtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identities\_of | [ IdentitiesOf](#identitiesof) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_default\_visible\_types | [ WithDefaultVisibleTypes](#withdefaultvisibletypes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ AssetSelectionQuery](#assetselectionquery) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.unsupported | [ Unsupported](#unsupported) | none |
### DomainDefinition
(synq.domains.v1.DomainDefinition)
| Field | Type | Description |
| - | - | - |
| parts | [repeated Part](#part) | none |
### DomainDefinition.Part
(synq.domains.v1.DomainDefinition.Part)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | part identifier |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier | [ Identifier](#identifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.dataproduct | [ DataproductIdentifier](#dataproductidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ AssetSelectionQuery](#assetselectionquery) | none |
### Domain
(synq.domains.v1.Domain)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| name | [ string](#string) | none |
| description | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | none |
| definition | [ DomainDefinition](#domaindefinition) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_id.parent\_id | [optional string](#string) | none |
| source | [ Source](#source) | Source for creation and management of domain. |
### SqlExpression
(synq.common.v1.SqlExpression)
SqlExpression carries a SQL string together with the placeholder tokens
the caller must substitute before executing it.
Empty `placeholders` means `sql` is byte-equal to what runs in the warehouse
and is safe to dispatch without further processing. When `placeholders` is
non-empty, every listed token appears literally in `sql` and must be replaced
(or otherwise interpreted) by the caller before execution.
| Field | Type | Description |
| - | - | - |
| sql | [ string](#string) | SQL string. Dialect matches the integration that owns the expression's parent resource. May contain placeholder tokens — see `placeholders`. |
| placeholders | [repeated SqlPlaceholder](#sqlplaceholder) | Placeholders present in `sql`. Order is undefined; each token is unique within the slice. Absent when `sql` is directly runnable. |
### SqlPlaceholder
(synq.common.v1.SqlPlaceholder)
SqlPlaceholder describes one substitution slot in a SqlExpression.
| Field | Type | Description |
| - | - | - |
| token | [ string](#string) | Literal token as it appears in `sql`, e.g. "\{from}". |
| description | [ string](#string) | Human- and AI-readable purpose of the placeholder. Should answer "what value should I substitute, and in what form?" without relying on out-of-band documentation. Example: "Lower bound of the metric query time window (inclusive, UTC timestamp)." |
### ScopeFilter
(synq.common.v1.ScopeFilter)
ScopeFilter defines include/exclude rules for scoping data warehouse queries.
Matching semantics:
* If include is non-empty, a (database, schema, table) tuple must match at least one include rule.
* If exclude is non-empty, the tuple must NOT match any exclude rule.
* Exclude takes precedence over include (exclude wins).
* Empty/unset ScopeFilter means accept all.
| Field | Type | Description |
| - | - | - |
| include | [repeated ScopeRule](#scoperule) | Include rules. If non-empty, only matching objects are accepted. |
| exclude | [repeated ScopeRule](#scoperule) | Exclude rules. Matching objects are rejected, even if they match an include rule. |
### ScopeRule
(synq.common.v1.ScopeRule)
ScopeRule is a multi-level pattern for matching data warehouse objects.
All non-empty fields must match for the rule to apply.
Empty field means "match anything" at that level (wildcard).
Patterns support glob syntax where \* matches zero or more characters.
Matching is case-insensitive.
| Field | Type | Description |
| - | - | - |
| database | [ string](#string) | Database-level pattern. Only meaningful for warehouses that expose more than one database/catalog per connection — Snowflake & Trino (catalog), Databricks (catalog), Athena (Glue catalog), DuckDB (database). Ignored by single-database/-project connections (Postgres, Redshift, MSSQL, Oracle, BigQuery project) and by MySQL/ClickHouse, which filter their namespace at the schema level below. |
| schema | [ string](#string) | Schema-level pattern. The level directly above a table. Maps to: dataset in BigQuery; database (namespace) in MySQL & ClickHouse; schema in Postgres/Redshift/Snowflake/Trino/MSSQL; owner in Oracle. |
| table | [ string](#string) | Table or view name pattern. |
### FolderOf
(synq.queries.v1.FolderOf)
Resolves the folder(s) containing a set of assets, for use as the folder
source of `InFolder` — i.e. "everything in the same folder as these assets".
Name the assets either the simple way (`entity_ids`, opaque asset ids) or with
a full `input_query` when they need to be computed (e.g. by name or type). Set
exactly one.
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Simplified source: the assets named directly by opaque entity id. Use this for the common "same folder as these specific assets" case; equivalent to an input\_query that lists the same ids. |
| input\_query | [ Query](#query) | General source: resolve the folder(s) of whatever assets this query matches (e.g. a name or type filter). Use when the assets can't be listed up front. |
| include\_all\_ancestors | [ bool](#bool) | When false (default): only each asset's immediate parent folder. When true: every ancestor folder up the hierarchy, so `InFolder` then also matches assets in those ancestor folders. |
### IdentitiesOf
(synq.queries.v1.IdentitiesOf)
Resolves to the identities of whatever the inner query matches.
The same real-world table is often described by several assets — a
transformation model and the warehouse table it materialises into are one
asset with several representations. Those representations form an identity
group. IdentitiesOf takes the assets matched by `input_query` and returns
their identity groups, so a selection made in one platform's terms also
covers the equivalent assets in every other platform.
The typical use is subtraction: "this warehouse database, except whatever my
transformation tool already owns" is an EXCEPT whose removal clause is an
IdentitiesOf over the transformation models. Without it the two selections
overlap on every materialised table.
| Field | Type | Description |
| - | - | - |
| input\_query | [ Query](#query) | The assets whose identities to resolve. Required. |
| limit\_to\_types | [repeated Type](#type) | Restrict the result to these types. When empty, only the other members of each identity group are returned. When set, an input asset that already has one of these types is returned as well. |
### InFolder
(synq.queries.v1.InFolder)
Resolves to entities located in one or more folders.
Give the folders exactly one way (they are mutually exclusive; if more than
one is set, `folders` wins, then `folder_of`, then `path`):
* `folders` — the preferred, human-authorable form: name each folder by
its root entity plus its ordered folder-name segments. No
opaque ids required.
* `folder_of` — "the same folder(s) as these assets": resolves the folder(s)
containing the assets matched by an inner query, then matches
everything in them. Lets you select siblings without naming a
folder.
* `path` — legacy opaque folder identifiers. Kept for back-compat;
prefer `folders`. Discover a folder's structured address
with the folders navigation API instead of hand-writing an id.
`deep` controls recursion (see its field comment).
InFolder lives here rather than in query\_parts.proto because FolderOf nests a
full Query, which would make query\_parts.proto import query.proto and form a
proto file import cycle.
| Field | Type | Description |
| - | - | - |
| path | [repeated string](#string) | Opaque folder identifiers (a folder's own entity id). `folders` is the structured, human-authorable alternative. When `path` is the only source set and `deep` is unset, `deep` defaults to true for back-compat with queries authored before `deep` existed. |
| folders | [repeated FolderRef](#folderref) | Structured, human-authorable folder addresses (root entity + folder-name segments). Take a `FolderRef` verbatim from a folder returned by the folders navigation API, or build one from names you can see in the catalog. |
| folder\_of | [ FolderOf](#folderof) | Resolve the folders to search from a set of assets, rather than naming them. Selects everything in the folder(s) that contain those assets. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deep.deep | [optional bool](#bool) | When false or unset, matches only the direct members of the folder(s). When true, matches the entire recursive subtree (members of subfolders too). |
Exception: when only the legacy `path` source is set and `deep` is unset, it defaults to true (preserves the behaviour of queries authored before this flag existed). With `folders` or `folder_of`, unset means false (shallow). |
### Query
(synq.queries.v1.Query)
Query is a structured query that can be used to find entities in the system.
It consists of multiple parts, each representing a different filter or sub-query.
The parts are combined using the operand to form the final result:
* AND (default): returns only entities matching ALL parts (intersection).
* OR: returns entities matching ANY part (union).
* EXCEPT: requires exactly 2 parts — returns entities from part\[0] that are NOT in part\[1].
* UPSTREAM: returns entities that are upstream (dependencies) of the entities matched by the parts.
* DOWNSTREAM: returns entities that are downstream (dependents) of the entities matched by the parts.
Queries can be nested by using a Query as a QueryPart, enabling complex compositions
like "upstream of all Snowflake tables".
| Field | Type | Description |
| - | - | - |
| parts | [repeated QueryPart](#querypart) | none |
| operand | [ QueryOperand](#queryoperand) | Defaults to AND if not specified. |
### Query.QueryPart
(synq.queries.v1.Query.QueryPart)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier\_list | [ IdentifierList](#identifierlist) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_name\_search | [ WithNameSearch](#withnamesearch) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_type | [ WithType](#withtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_annotation | [ WithAnnotation](#withannotation) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_dataproduct | [ InDataproduct](#indataproduct) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_data\_platform | [ InDataPlatform](#indataplatform) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_data\_platform\_type | [ WithDataPlatformType](#withdataplatformtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_folder | [ InFolder](#infolder) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_domain | [ InDomain](#indomain) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identities\_of | [ IdentitiesOf](#identitiesof) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_default\_visible\_types | [ WithDefaultVisibleTypes](#withdefaultvisibletypes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.entity\_ids | [ EntityIds](#entityids) | Pin specific entities by the ids the API returned for them. Read back as `identifier_list`, with each id on the identifier it names. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ Query](#query) | Nested sub-query, enabling recursive composition of filters and operands. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.unsupported | [ Unsupported](#unsupported) | Read-only. Represents internal query types that cannot be expressed in the public API. Do not construct these directly — they are returned when translating from internal queries. |
### DescribeResolverQlRequest
(synq.queries.v1.DescribeResolverQlRequest)
### DescribeResolverQlResponse
(synq.queries.v1.DescribeResolverQlResponse)
| Field | Type | Description |
| - | - | - |
| functions | [repeated ResolverQlFunction](#resolverqlfunction) | Every function the language has, ordered by name. |
| categories | [repeated string](#string) | The categories a function's `category` names, in the order the product presents them. Group the catalog by this rather than sorting the names. |
| enum\_domains | [map EnumDomainsEntry](#enumdomainsentry) | The accepted values of every enum-typed argument, keyed by the name an argument's `enum_domain` carries. A domain absent here places no restriction on the argument beyond its type. |
### DescribeResolverQlResponse.EnumDomainsEntry
(synq.queries.v1.DescribeResolverQlResponse.EnumDomainsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ResolverQlEnumDomain](#resolverqlenumdomain) | none |
### ResolveQueryRequest
(synq.queries.v1.ResolveQueryRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.resolver\_ql | [ string](#string) | ResolverQL expression to compile and evaluate. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.query | [ Query](#query) | Structured query to evaluate. |
| pagination | [ Pagination](#pagination) | Pagination over the matched entity set. `page_size` defaults per API when unset; pass the previous response's `page_info.last_id` as the cursor to fetch the next page. |
### ResolveQueryResponse
(synq.queries.v1.ResolveQueryResponse)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated Identifier](#identifier) | Opaque identifiers of the matched entities, one page worth. Round-trip each id verbatim to the rest of the entities API. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
| rendered\_resolver\_ql | [ string](#string) | The resolved selection rendered as canonical ResolverQL. Empty when the selection has no ResolverQL representation. |
### TranslateResolverQlRequest
(synq.queries.v1.TranslateResolverQlRequest)
| Field | Type | Description |
| - | - | - |
| resolver\_ql | [ string](#string) | ResolverQL expression to compile, for example `with_type("TABLE")`. |
### TranslateResolverQlResponse
(synq.queries.v1.TranslateResolverQlResponse)
| Field | Type | Description |
| - | - | - |
| query | [ Query](#query) | The compiled structured query. Pass it verbatim to any public API that accepts a `synq.queries.v1.Query` (for example a folder browse `filter`), or to `ResolveQuery.query` to evaluate it. |
| rendered\_resolver\_ql | [ string](#string) | The compiled query rendered back to canonical ResolverQL. Empty when the query has no ResolverQL representation (legacy / deprecated operands). |
### ResolverQlArg
(synq.queries.v1.ResolverQlArg)
One argument of a function, positional or keyword.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | The argument's name. For a keyword argument this is the spelling written before the `=`. |
| type | [ string](#string) | The value shape the argument takes — a string, a list of strings, a boolean, a nested query. Names are the language's own and are stable. |
| required | [ bool](#bool) | Whether the call fails to compile without this argument. |
| enum\_domain | [ string](#string) | Names an entry in `enum_domains` when the argument accepts only values from a fixed vocabulary. Empty when any value of `type` is accepted. |
| doc | [ string](#string) | What this argument does, in one sentence. Empty when the function's own doc says all there is to say. |
### ResolverQlEnumDomain
(synq.queries.v1.ResolverQlEnumDomain)
The values one enum-typed argument accepts.
| Field | Type | Description |
| - | - | - |
| values | [repeated string](#string) | Every value the domain enumerates, lowercase. A value is matched case-insensitively, so a caller holding an upper-case spelling can send it as it is. |
| pattern | [ string](#string) | An RE2 pattern of values accepted in **addition** to `values`, matched against the lowercased value. Empty when the domain accepts nothing beyond the list. |
It exists because one domain is not fully enumerated: the entity types a workspace defines itself are a reserved band of the asset-type vocabulary rather than members of it, spelled `custom_type_`, and `values` today holds only the built-in half. A client that validates against `values` alone rejects a query this server compiles.
Keep honouring the pattern even once a domain enumerates more than it does today: it is what makes a value the list has not caught up with still acceptable. |
### ResolverQlFunction
(synq.queries.v1.ResolverQlFunction)
One function of the ResolverQL language, as this deployment compiles it.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | The name the query writes, lowercase and canonical. Function names compile case-insensitively, so `WITH_TYPE` reaches the same function as `with_type`. |
| category | [ string](#string) | Which job this function does, one of the values in `categories`. Use it to group the catalog the way the product does rather than inventing an order. |
| doc | [ string](#string) | What the function selects, in one sentence. Written for a person choosing between functions; safe to show verbatim. |
| variadic | [ bool](#bool) | Whether the function accepts more arguments than it declares. A variadic function with no declared arguments — `and`, `or` — takes any number of sub-queries. |
| default\_kwarg | [ string](#string) | The keyword argument a bare positional value is routed to when the call names none. Empty when the function has no such argument. |
| positional | [repeated ResolverQlArg](#resolverqlarg) | Arguments in the order they are written before any `name=value` argument. |
| kwargs | [repeated ResolverQlArg](#resolverqlarg) | Arguments written as `name=value`, ordered by name. An argument that can be written either way appears in `positional` only. |
### EntityIds
(synq.queries.v1.EntityIds)
Selects exactly the entities you list, by the `entity_id` the API returned for
each of them.
This is the pin-these-specific-entities part of a query: the assets a user
ticked in a list, the results of a search you just ran, the entities another
API call handed you. Because you pass the ids back as they were given, you do
not need to know what kind of entity each one is, or how its address in the
source system is spelled.
`identifier_list` is the alternative, and the choice between them is about
what you have in hand:
* `entity_ids` — you are holding ids the API gave you. Pass them
through unchanged.
* `identifier_list` — you are naming entities by their address in the source
system (a BigQuery project/dataset/table, a dbt model,
a Looker explore), for example because a person or a
config file wrote it down rather than an API call.
**Reading back a selection you wrote with `entity_ids`.** Selections are always
returned as `identifier_list`, whichever of the two you sent — one uniform
shape, so integrations built before `entity_ids` existed keep reading stored
queries unchanged. Your ids are not lost in the process: every returned
identifier carries its `entity_id`, so you can match each one back to what you
sent, and compare a saved selection against the one you are about to save. The
returned id is the entity's canonical form, which can differ from an equivalent
spelling you sent for the same entity.
**An id that matches nothing selects nothing**, and is not an error — so a
saved query keeps working after one of the entities it names is deleted or
renamed, rather than failing every time it is read or resolved.
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | The entities to select. An id may not be blank. |
An empty list selects no entities, exactly as an empty `identifier_list` does. Be deliberate about it, because it is not the same as leaving the part out: a part that selects nothing still counts, so it empties an AND, adds nothing to an OR, and removes nothing from an EXCEPT. If what you mean is "do not restrict on specific entities", omit the part instead of sending it empty.
Ids may be up to 1024 characters — the same limit as `Identifier.entity_id`, where you read them from, so any id the API returned is accepted here. |
### IdentifierList
(synq.queries.v1.IdentifierList)
Resolves to entities matching any of the given identifiers.
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### InDataPlatform
(synq.queries.v1.InDataPlatform)
Resolves to all entities that belong to the given data platform.
| Field | Type | Description |
| - | - | - |
| identifier | [ DataPlatformIdentifier](#dataplatformidentifier) | none |
### InDataproduct
(synq.queries.v1.InDataproduct)
Resolves to all the assets that belong to the given data product.
| Field | Type | Description |
| - | - | - |
| identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
### InDomain
(synq.queries.v1.InDomain)
Resolves to all entities in the given data domain.
| Field | Type | Description |
| - | - | - |
| domain\_id | [ string](#string) | none |
### Unsupported
(synq.queries.v1.Unsupported)
Collects all the parts of the query that are currently unsupported in translation.
These represent an internal structure that are not yet completely available on the API.
| Field | Type | Description |
| - | - | - |
| query\_json | [ string](#string) | none |
### WithAnnotation
(synq.queries.v1.WithAnnotation)
Resolves to all entities that contain the annotation with at least one of the provided accepted values.
In case of no given accepted values, all entities that carry the given annotation are returned.
The name and the values match case-insensitively.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_accepted\_value.accepted\_value | [optional string](#string) | none |
| accepted\_values | [repeated string](#string) | none |
### WithDataPlatformType
(synq.queries.v1.WithDataPlatformType)
| Field | Type | Description |
| - | - | - |
| types | [repeated DataPlatformType](#dataplatformtype) | none |
### WithDefaultVisibleTypes
(synq.queries.v1.WithDefaultVisibleTypes)
Resolves to every asset of a type the catalog shows by default — the
"everything" starting set.
Use it as the base of a subtraction, e.g. an EXCEPT query whose first part is
WithDefaultVisibleTypes and whose second part is what to leave out. On its own
it selects the whole catalog, so it is rarely useful as the only part of a
query.
"Default visible" excludes the supporting asset types the catalog hides unless
asked for (columns, tests, and similar), so this is not literally every entity.
### WithNameSearch
(synq.queries.v1.WithNameSearch)
Resolves to all entities matching the given name search.
| Field | Type | Description |
| - | - | - |
| search\_query | [ string](#string) | none |
### WithType
(synq.queries.v1.WithType)
Resolves to all the entities that have the provided type, or carry one of the
provided traits.
| Field | Type | Description |
| - | - | - |
| type | [ Type](#type) | none |
| types | [repeated Type](#type) | none |
| traits | [repeated string](#string) | Traits to select by, instead of — or alongside — naming types. |
A trait is a property a type has, so `is_monitorable` means "everything a monitor can run on" without listing the forty-odd types that satisfy it today. The list it stands for is worked out each time the selection runs, so a selection saved this way covers a new platform's tables as soon as we support them, and you never have to revisit it.
Entities carrying any of the traits match. When `types` is also set the two narrow each other: the entity must be one of the named types AND carry one of the traits.
Accepted values, and what each one means, come from the selection-query grammar the API publishes (`describe_resolver_ql`, or the query editor's reference). A name that is not in it selects nothing rather than being ignored, so a typo narrows the result to empty instead of widening it — check a trait against the grammar before storing a selection. In a query written as text this is `with_type(traits="is_monitorable")`, and there a name outside the grammar is rejected outright.
A trait may also be a type alias, spelled `alias:` where `` is one of the aliases the grammar lists among the entity types, e.g. `alias:table`. It selects every type the alias stands for, worked out each time the selection runs like any other trait. A selection written as `with_type("table")` is stored this way and reads back with `traits: ["alias:table"]`; writing that back keeps the same selection. In a query written as text an alias is always `with_type("table")`, never `traits="alias:table"`. |
### WithType.Type
(synq.queries.v1.WithType.Type)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) entity\_type.default | [ EntityType](#entitytype) | Default Coalesce Quality types |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) entity\_type.custom | [ int32](#int32) | Custom types as defined through synq.entities.custom.v1.TypesService |
### GenerateQueryRequest
(synq.queries.v1.GenerateQueryRequest)
| Field | Type | Description |
| - | - | - |
| description | [ string](#string) | none |
### GenerateQueryResponse
(synq.queries.v1.GenerateQueryResponse)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_query.query | [optional Query](#query) | The public Query proto. Absent when unsupported. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_resolver\_ql.resolver\_ql | [optional string](#string) | The generated ResolverQL query string. Empty when unsupported. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_unsupported\_reason.unsupported\_reason | [optional string](#string) | When the LLM determines the request cannot be expressed as a query, this field contains the reason(s). The other fields will be empty. |
### Incident
(synq.incidents.v1.Incident)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | ID of the incident. |
| index | [ int32](#int32) | Index of the incident (for human-readable references). |
| name | [ string](#string) | Name of the incident. |
| issue\_ids | [repeated string](#string) | IDs of issues associated with this incident. |
| created\_by | [ Actor](#actor) | Actor who created the incident. |
| owner | [ User](#user) | Owner of the incident. |
| created\_at | [ Timestamp](#timestamp) | Time at which the incident was created. |
| updated\_by | [ Actor](#actor) | Actor who last updated the incident. |
| updated\_at | [ Timestamp](#timestamp) | Time at which the incident was last updated. |
| state | [ State](#state) | State of the incident. |
| comments | [repeated IncidentComment](#incidentcomment) | List of comments posted on the incident. |
### IncidentComment
(synq.incidents.v1.IncidentComment)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| message | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| actor | [ Actor](#actor) | none |
### AddIssuesToIncidentRequest
(synq.incidents.v1.AddIssuesToIncidentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to add issues to |
| actor | [ Actor](#actor) | Actor adding the issues |
| at | [ Timestamp](#timestamp) | Time at which the issues were added. Defaults to the current time. |
| issue\_ids | [repeated string](#string) | IDs of issues to add to the incident |
### AddIssuesToIncidentResponse
(synq.incidents.v1.AddIssuesToIncidentResponse)
### AssignIncidentOwnerRequest
(synq.incidents.v1.AssignIncidentOwnerRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to assign the owner to |
| owner\_email | [ string](#string) | none |
| actor | [ Actor](#actor) | none |
### AssignIncidentOwnerResponse
(synq.incidents.v1.AssignIncidentOwnerResponse)
### BatchGetIncidentsRequest
(synq.incidents.v1.BatchGetIncidentsRequest)
| Field | Type | Description |
| - | - | - |
| incident\_ids | [repeated string](#string) | IDs of the incidents to get |
### BatchGetIncidentsResponse
(synq.incidents.v1.BatchGetIncidentsResponse)
| Field | Type | Description |
| - | - | - |
| incidents | [map IncidentsEntry](#incidentsentry) | Incidents keyed by their IDs |
### BatchGetIncidentsResponse.IncidentsEntry
(synq.incidents.v1.BatchGetIncidentsResponse.IncidentsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Incident](#incident) | none |
### CancelIncidentRequest
(synq.incidents.v1.CancelIncidentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to cancel |
| actor | [ Actor](#actor) | Actor cancelling the incident |
| at | [ Timestamp](#timestamp) | Time at which the incident was cancelled. Defaults to the current time. |
### CancelIncidentResponse
(synq.incidents.v1.CancelIncidentResponse)
### CloseIncidentRequest
(synq.incidents.v1.CloseIncidentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to close |
| actor | [ Actor](#actor) | Actor closing the incident |
| at | [ Timestamp](#timestamp) | Time at which the incident was closed. Defaults to the current time. |
### CloseIncidentResponse
(synq.incidents.v1.CloseIncidentResponse)
### CreateIncidentRequest
(synq.incidents.v1.CreateIncidentRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | IDs of issues to add to the incident |
| name | [ string](#string) | Name of the incident |
| actor | [ Actor](#actor) | Actor creating the incident |
| at | [ Timestamp](#timestamp) | Time at which the incident was created. Defaults to the current time. |
| group\_ids | [repeated string](#string) | Optional group IDs associated with the incident |
| owner\_email | [ string](#string) | none |
### CreateIncidentResponse
(synq.incidents.v1.CreateIncidentResponse)
| Field | Type | Description |
| - | - | - |
| incident | [ Incident](#incident) | none |
### DeleteCommentRequest
(synq.incidents.v1.DeleteCommentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident the comment is on. |
| comment\_id | [ string](#string) | ID of the comment to delete, as returned by PostComment or read from the incident's comments. |
| actor | [ Actor](#actor) | Actor deleting the comment. Must match the actor that posted it. |
### DeleteCommentResponse
(synq.incidents.v1.DeleteCommentResponse)
### ListIncidentsRequest
(synq.incidents.v1.ListIncidentsRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_time.time | [optional Time](#time) | Time range filter |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sort.sort | [optional Sort](#sort) | Sort options |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination |
| states | [repeated State](#state) | Filter by states |
| affected\_entities | [repeated Identifier](#identifier) | Filter by affected entities |
### ListIncidentsRequest.Sort
(synq.incidents.v1.ListIncidentsRequest.Sort)
| Field | Type | Description |
| - | - | - |
| sort\_by | [ Field](#field) | none |
| order | [ Order](#order) | none |
### ListIncidentsRequest.Time
(synq.incidents.v1.ListIncidentsRequest.Time)
| Field | Type | Description |
| - | - | - |
| field | [ Field](#field) | none |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
### ListIncidentsResponse
(synq.incidents.v1.ListIncidentsResponse)
| Field | Type | Description |
| - | - | - |
| incidents | [repeated Incident](#incident) | none |
### MergeIncidentsRequest
(synq.incidents.v1.MergeIncidentsRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the primary incident to merge into |
| actor | [ Actor](#actor) | Actor performing the merge |
| at | [ Timestamp](#timestamp) | Time at which the incidents were merged. Defaults to the current time. |
| other\_incident\_ids | [repeated string](#string) | IDs of other incidents to merge into the primary incident |
### MergeIncidentsResponse
(synq.incidents.v1.MergeIncidentsResponse)
### PostCommentRequest
(synq.incidents.v1.PostCommentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to post a comment on. |
| actor | [ Actor](#actor) | Actor posting the comment. |
| comment | [ string](#string) | Comment to post. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_at.at | [optional Timestamp](#timestamp) | Time at which the comment was posted. Defaults to the current time when not provided. |
### PostCommentResponse
(synq.incidents.v1.PostCommentResponse)
| Field | Type | Description |
| - | - | - |
| comment\_id | [ string](#string) | none |
### RemoveIssuesFromIncidentRequest
(synq.incidents.v1.RemoveIssuesFromIncidentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to remove issues from |
| actor | [ Actor](#actor) | Actor removing the issues |
| at | [ Timestamp](#timestamp) | Time at which the issues were removed. Defaults to the current time. |
| issue\_ids | [repeated string](#string) | IDs of issues to remove from the incident |
### RemoveIssuesFromIncidentResponse
(synq.incidents.v1.RemoveIssuesFromIncidentResponse)
### RenameIncidentRequest
(synq.incidents.v1.RenameIncidentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to rename |
| actor | [ Actor](#actor) | Actor renaming the incident |
| at | [ Timestamp](#timestamp) | Time at which the incident was renamed. Defaults to the current time. |
| name | [ string](#string) | New name for the incident |
### RenameIncidentResponse
(synq.incidents.v1.RenameIncidentResponse)
### SplitIncidentRequest
(synq.incidents.v1.SplitIncidentRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the incident to split |
| actor | [ Actor](#actor) | Actor performing the split |
| at | [ Timestamp](#timestamp) | Time at which the incident was split. Defaults to the current time. |
| split\_issue\_ids | [repeated string](#string) | IDs of issues to move to the new incident |
| split\_incident\_name | [ string](#string) | Name for the new incident |
### SplitIncidentResponse
(synq.incidents.v1.SplitIncidentResponse)
| Field | Type | Description |
| - | - | - |
| incident | [ Incident](#incident) | none |
| split\_incident | [ Incident](#incident) | none |
### UpdateIncidentStateRequest
(synq.incidents.v1.UpdateIncidentStateRequest)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | none |
| actor | [ Actor](#actor) | Actor updating the incident state |
| at | [ Timestamp](#timestamp) | Time at which the incident state was updated. Defaults to the current time. |
| state | [ State](#state) | New state for the incident |
### UpdateIncidentStateResponse
(synq.incidents.v1.UpdateIncidentStateResponse)
| Field | Type | Description |
| - | - | - |
| incident | [ Incident](#incident) | none |
### BatchGetEntityOverlaysRequest
(synq.overlays.v1.BatchGetEntityOverlaysRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | The entities to ask about: 1 to 1000 ids, no duplicates. |
An `entity_id` is opaque. Take one from any listing or search response, or from `synq.entities.resolve.v1.IdentifierResolveService` if what you hold is a warehouse table name or a model name, and pass it back unchanged. Do not build one yourself or parse one apart — the format carries no meaning you can rely on and is not part of this contract. |
### BatchGetEntityOverlaysResponse
(synq.overlays.v1.BatchGetEntityOverlaysResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated EntityOverlays](#entityoverlays) | One entry per requested entity, in the order they were requested. |
### BatchGetOverlayMemberCountsRequest
(synq.overlays.v1.BatchGetOverlayMemberCountsRequest)
| Field | Type | Description |
| - | - | - |
| overlay\_entity\_ids | [repeated string](#string) | The overlays to count: 1 to 1000 ids, no duplicates. |
Opaque ids, as returned by `BatchGetEntityOverlays` or by the listing calls of the service that defines the overlay. Data products and owners may be mixed freely in one request. |
### BatchGetOverlayMemberCountsResponse
(synq.overlays.v1.BatchGetOverlayMemberCountsResponse)
| Field | Type | Description |
| - | - | - |
| overlays | [repeated OverlayMemberCount](#overlaymembercount) | One entry per requested overlay, in the order they were requested. |
### CoverageSelection
(synq.overlays.v1.CoverageSelection)
CoverageSelection names the assets a coverage question is asked about.
It is a candidate set, not a filter on overlays: nothing outside it is
considered, and an asset inside it is reported whether anything claims it or
not. `with_type("dbt_model")` asks about your models, `in_folder(...)` asks
about one part of the warehouse, and an empty candidate set is a valid
question with the answer zero.
Choose the candidate types deliberately — it is the difference between a
useful answer and an unreadable one. The two dimensions want different sets.
Data products group the things you monitor, so a product-coverage question
usually asks about tables, views and models; left unrestricted the answer is
mostly monitors, tests and recon cases, which belong to no product by design
and bury the assets you were looking for. Owner coverage is the opposite: a
check very often should have someone responsible for it, so include those
types when asking about owners.
So a governance audit is normally several narrow selections rather than one
broad one — which is also what `GetCoverageSummary` takes, one per bucket.
A selection resolving to more than 100,000 assets is rejected with
INVALID\_ARGUMENT naming the limit; narrow it and ask again.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.resolver\_ql | [ string](#string) | The candidate query in resolver query language. The fuller of the two forms — everything the structured query expresses is expressible here, and several filters are only expressible here. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.public\_query | [ Query](#query) | The candidate query in structured form, as the filter builder produces it. Use `resolver_ql` for anything it cannot express. |
### CoverageSummary
(synq.overlays.v1.CoverageSummary)
CoverageSummary is one selection's totals.
The counts overlap rather than partition: an asset in a product and with an
owner is in `with_dataproduct`, in `with_owner` and in `with_both`. Only
`with_neither` is disjoint from the rest. So the uncovered counts are
`total - with_dataproduct` and `total - with_owner`, and "in a product but
unowned" is `with_dataproduct - with_both`.
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | The key of the selection this row answers for, echoed back. |
| total | [ int64](#int64) | Candidates in the selection. |
| with\_dataproduct | [ int64](#int64) | Candidates in at least one data product. |
| with\_owner | [ int64](#int64) | Candidates with at least one owner. |
| with\_both | [ int64](#int64) | Candidates in at least one data product *and* with at least one owner. |
| with\_neither | [ int64](#int64) | Candidates nothing claims — the gap a coverage audit is looking for. |
### EntityOverlays
(synq.overlays.v1.EntityOverlays)
EntityOverlays is one entity and the overlays claiming it, one list per kind.
Kinds are separate lists rather than one tagged list because what you can do
with an id depends on its kind: an owner id reads through the owners service, a
data-product id through the data-products service.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The entity this entry answers for, echoed back from the request. |
| owner\_entity\_ids | [repeated string](#string) | Owners responsible for this entity — the parties an alert about it reaches. Read their names and contact channels with `synq.owners.v1.OwnersService`. |
Empty when no owner claims the entity, and also when your credential may not read owners. Order is not significant and may change between calls; sort if you display them. |
\| dataproduct\_entity\_ids | [repeated string](#string) | Data products this entity belongs to. Read their names and definitions with `synq.dataproducts.v2.DataproductsService`.
Empty when the entity is in no data product, and also when your credential may not read data products. Order is not significant and may change between calls; sort if you display them. |
### GetCoverageSummaryRequest
(synq.overlays.v1.GetCoverageSummaryRequest)
| Field | Type | Description |
| - | - | - |
| selections | [repeated NamedSelection](#namedselection) | The candidate sets to count: 1 to 20, with distinct keys. |
| freshness | [ Freshness](#freshness) | Where the answer is computed from. Defaults to the recomputed membership. |
`FRESHNESS_LIVE` re-resolves every definition once for the whole request, not once per selection, so several selections cost little more than one. |
### GetCoverageSummaryResponse
(synq.overlays.v1.GetCoverageSummaryResponse)
| Field | Type | Description |
| - | - | - |
| summaries | [repeated CoverageSummary](#coveragesummary) | One entry per requested selection, in the order they were requested. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_members\_computed\_at.members\_computed\_at | [optional Timestamp](#timestamp) | When the recomputation behind these totals last ran to completion — the start of the most recent successful pass over the workspace's overlays. |
Absent under `FRESHNESS_LIVE`, and absent when the workspace has no overlay of a readable kind, or none has been computed yet. |
### ListEntityCoverageRequest
(synq.overlays.v1.ListEntityCoverageRequest)
| Field | Type | Description |
| - | - | - |
| selection | [ CoverageSelection](#coverageselection) | The assets to report on. Required. |
| filter | [ CoverageFilter](#coveragefilter) | Which coverage state to keep. Defaults to all candidates. |
A filter naming a kind your credential may not read is PERMISSION\_DENIED rather than an empty list. |
\| freshness | [ Freshness](#freshness) | Where the answer is computed from. Defaults to the recomputed membership. |
\| pagination | [ Pagination](#pagination) | Pagination over the candidates matching `filter`. |
### ListEntityCoverageResponse
(synq.overlays.v1.ListEntityCoverageResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated EntityOverlays](#entityoverlays) | One page of matching candidates, each with the overlays claiming it. |
The same message `BatchGetEntityOverlays` returns: a row under `COVERAGE_FILTER_WITHOUT_DATAPRODUCT` has an empty `dataproduct_entity_ids` and may still name owners, which is how "owned but in no product" reads without a second call. |
\| total\_count | [ int64](#int64) | How many candidates match `filter` across every page. Independent of pagination — the number to show beside a list, not the length of it. |
\| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
\| rendered\_resolver\_ql | [ string](#string) | The candidate selection rendered back to canonical resolver query language, whichever form it was sent in. Useful to log what was actually asked, and to lift a query built with the structured form into one you can hand-edit.
Empty when the selection has no resolver-QL representation; the answer is unaffected. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_members\_computed\_at.members\_computed\_at | [optional Timestamp](#timestamp) | When the recomputation behind this answer last ran to completion — the start of the most recent successful pass over the workspace's overlays. Every overlay is recomputed on each pass, so this is the age of the whole answer rather than of any one overlay in it.
This is the number to alert on: it stops advancing as soon as the recomputation stops succeeding, whether or not any membership has changed.
Absent under `FRESHNESS_LIVE` (nothing was read from an index), and absent when no overlay of a readable kind exists in the workspace, or none has been computed yet (there is no membership to be stale). |
### NamedSelection
(synq.overlays.v1.NamedSelection)
NamedSelection is one candidate set and the label you want its row keyed by.
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | Your own label for this selection, echoed back on the matching summary. It is never interpreted — use whatever names the bucket in your dashboard. Must be unique within the request. |
| selection | [ CoverageSelection](#coverageselection) | The assets this row counts. Required. |
### OverlayMemberCount
(synq.overlays.v1.OverlayMemberCount)
| Field | Type | Description |
| - | - | - |
| overlay\_entity\_id | [ string](#string) | The overlay this entry answers for, echoed back from the request. |
| member\_count | [ int64](#int64) | How many distinct entities belong to it. |
Zero covers every case where there is nothing to count: an overlay with no members, an id that does not exist, and an id your credential may not read. If you need to tell those apart, read the overlay itself through the service that defines it. |
### SnowflakeCloudConf
(synq.integrations.v1.SnowflakeCloudConf)
Snowflake cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | Snowflake username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.password | [ string](#string) | Password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.key\_pair | [ SnowflakeKeyPairAuth](#snowflakekeypairauth) | Key-pair authentication. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_account.account | [optional string](#string) | Account identifier (e.g. "xy12345.us-east-1") without scheme or trailing slash. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_warehouse.warehouse | [optional string](#string) | Warehouse to run queries on. |
| databases | [repeated string](#string) | Databases to monitor and ingest metadata from. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_role.role | [optional string](#string) | Role to use for metadata access and queries. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs from the ACCOUNT\_USAGE schema. |
| fetch\_query\_logs\_databases | [repeated string](#string) | Databases to fetch query logs from (defaults to all configured databases). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_no\_get\_ddl.no\_get\_ddl | [optional bool](#bool) | Skip GET\_DDL for table definitions (use query logs instead). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_tasks.fetch\_tasks | [optional bool](#bool) | Ingest Snowflake Tasks metadata. |
| fetch\_tasks\_databases | [repeated string](#string) | Databases to retrieve tasks from (defaults to all configured databases). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_tasks\_executions\_from\_account\_usage.fetch\_tasks\_executions\_from\_account\_usage | [optional bool](#bool) | Use ACCOUNT\_USAGE.TASK\_HISTORY for task execution history. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_tasks\_executions\_duration.fetch\_tasks\_executions\_duration | [optional Duration](#duration) | Time range for fetching task execution history (minimum 5 minutes). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_table\_lineage.fetch\_table\_lineage | [optional bool](#bool) | Produce table-level lineage via GET\_LINEAGE (Enterprise edition or higher). |
| fetch\_table\_lineage\_databases | [repeated string](#string) | Databases to fetch upstream lineage from (defaults to `databases`). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_account\_usage\_db.account\_usage\_db | [optional string](#string) | Database where the ACCOUNT\_USAGE schema lives (defaults to "SNOWFLAKE"). |
### SnowflakeKeyPairAuth
(synq.integrations.v1.SnowflakeKeyPairAuth)
Snowflake key-pair authentication.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_private\_key\_secret.private\_key\_secret | [optional string](#string) | PEM-encoded private key (PKCS#8). Write-only — masked on reads. Required when using key-pair auth. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_public\_key.public\_key | [optional string](#string) | Public key corresponding to the private key. For a Coalesce Quality–managed key pair this is returned so you can register it on the Snowflake user; when you supply your own key it is optional. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_private\_key\_passphrase.private\_key\_passphrase | [optional string](#string) | Optional passphrase for an encrypted private key. Write-only — masked on reads. |
### DatabricksCloudConf
(synq.integrations.v1.DatabricksCloudConf)
Databricks cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_workspace\_url.workspace\_url | [optional string](#string) | Workspace URL (e.g. "[https://your-workspace.cloud.databricks.com](https://your-workspace.cloud.databricks.com)"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.token | [ string](#string) | Personal access token. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.oauth\_m2m | [ DatabricksOAuthM2M](#databricksoauthm2m) | OAuth 2.0 machine-to-machine (service principal) auth. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_warehouse\_id.warehouse\_id | [optional string](#string) | SQL warehouse id (or cluster id) used to execute queries. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs from system tables. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_table\_metrics.fetch\_table\_metrics | [optional bool](#bool) | Run ANALYZE TABLE to refresh and collect statistics. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_table\_metrics\_scan.fetch\_table\_metrics\_scan | [optional bool](#bool) | Run ANALYZE TABLE ... SCAN for detailed metrics incl. row counts (more resource-intensive). |
| catalog\_blocklist | [repeated string](#string) | Catalog name patterns to exclude from ingestion. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_table\_tags.fetch\_table\_tags | [optional bool](#bool) | Retrieve Unity Catalog table tags. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_show\_create\_table.use\_show\_create\_table | [optional bool](#bool) | Use SHOW CREATE TABLE for table DDL (otherwise extracted from query logs). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_disable\_unity\_table\_lineage.disable\_unity\_table\_lineage | [optional bool](#bool) | Disable retrieval of Unity Catalog table-to-table lineage. Coalesce Quality already computes lineage from query logs, so the Unity Catalog edges can be redundant noise. When unset/false, Unity Catalog table-to-table lineage is retrieved and published (the default). |
### DatabricksOAuthM2M
(synq.integrations.v1.DatabricksOAuthM2M)
Databricks OAuth 2.0 machine-to-machine (service principal) credentials.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_client\_id.client\_id | [optional string](#string) | OAuth client id of the service principal. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_client\_secret.client\_secret | [optional string](#string) | OAuth client secret. Write-only — masked on reads. |
### PostgresCloudConf
(synq.integrations.v1.PostgresCloudConf)
PostgreSQL cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | Username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Host address or IP. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Port (typically 5432). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Database to connect to and ingest metadata from. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Table name for storing SQL test audit logs (e.g., 'sql\_test\_audit'). Auto-creation quotes the value as a single identifier, so a schema-qualified name only works against a table you have created yourself. |
### OracleCloudConf
(synq.integrations.v1.OracleCloudConf)
Oracle Database cloud-managed connection. Oracle connections are scoped to a
Pluggable Database (PDB) via the service name.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Host address or IP (OCI Autonomous DB e.g. "adb.eu-frankfurt-1.oraclecloud.com"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Listener port (typically 1521; OCI Autonomous DB with mTLS uses 1522). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_service\_name.service\_name | [optional string](#string) | Service name / PDB name (OCI Autonomous DB e.g. "mydb\_high"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_username.username | [optional string](#string) | Username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ssl.ssl | [optional bool](#bool) | Enable TLS/SSL (TCPS). Required for OCI Autonomous Database. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ssl\_verify.ssl\_verify | [optional bool](#bool) | Verify the server certificate when SSL is enabled. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs (V\$SQL by default, DBA\_HIST with the Diagnostics Pack). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_diagnostics\_pack.use\_diagnostics\_pack | [optional bool](#bool) | Use the Oracle Diagnostics Pack (AWR) for persistent query-log history (requires a license; included with OCI Autonomous Database). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
### MSSQLCloudConf
(synq.integrations.v1.MSSQLCloudConf)
Microsoft SQL Server / Azure SQL Database cloud-managed connection. SQL Server
connections are scoped to a single database; create one integration per
database.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Hostname or IP (Azure SQL e.g. "yourserver.database.windows.net"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Port (typically 1433). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Database to connect to and ingest metadata from. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_username.username | [optional string](#string) | Login username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Login password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_trust\_cert.trust\_cert | [optional bool](#bool) | Skip TLS server certificate verification (not recommended for Azure SQL). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_encrypt.encrypt | [optional string](#string) | Encryption mode: "true" (default, required for Azure SQL), "false", "disable". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs from Query Store (must be enabled on the database; Azure SQL has it on by default). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scope.scope | [optional ScopeFilter](#scopefilter) | Include/exclude scope filter (by schema and table; database-level filtering does not apply since the connection targets one database). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
### AthenaOutputs
(synq.integrations.v1.AthenaOutputs)
Athena generated outputs.
| Field | Type | Description |
| - | - | - |
| account\_id | [ string](#string) | AWS account id resolved from the supplied credentials. |
| principal\_arn | [ string](#string) | ARN of the IAM principal Coalesce Quality authenticates as. Reference it in your Athena / Glue resource policies. |
### BigQueryOutputs
(synq.integrations.v1.BigQueryOutputs)
BigQuery generated outputs.
| Field | Type | Description |
| - | - | - |
| service\_account\_email | [ string](#string) | Email of the service account derived from the supplied credentials. Grant this principal access to the datasets you want Coalesce Quality to read. |
### Capabilities
(synq.integrations.v1.Capabilities)
Capabilities advertise which actions are valid for an integration in its
current state, so a client can decide what to offer before calling.
Capabilities are a hint computed from the integration's type and current
state; the server still enforces every action. A `false` flag means the
action would be rejected right now (unsupported for this type, or not
applicable in the current state — for example `can_enable` is false for an
already-enabled integration).
| Field | Type | Description |
| - | - | - |
| can\_patch | [ bool](#bool) | The configuration can be changed via `UpdateIntegration`. False for read-only / externally-owned integrations. |
| can\_refresh | [ bool](#bool) | An ad-hoc refresh can be triggered via `RefreshIntegration`. |
| can\_disable | [ bool](#bool) | The integration can be disabled via `DisableIntegration` (true only when currently enabled). |
| can\_enable | [ bool](#bool) | The integration can be enabled via `EnableIntegration` (true only when currently disabled). |
| can\_delete | [ bool](#bool) | The integration can be deleted via `DeleteIntegration`. |
| is\_dwh | [ bool](#bool) | Whether this is a data warehouse or database connection (rather than a transformation tool, catalog, etc.). Querying features — data preview, SQL tests, and reconciliation — are only available for these. |
| can\_reconcile\_deploy | [ bool](#bool) | Whether scheduled and API-triggered reconciliations can run against this integration. True only when it is an enabled data warehouse and your workspace has enabled reconciliation for it. These runs use the integration's workspace-level credentials, so this does not depend on any individual user's personal connection. |
### Integration
(synq.integrations.v1.Integration)
Integration is a connection from Coalesce Quality to one of your data
systems (a warehouse, database, or transformation tool).
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Server-assigned unique identifier (UUID). Use it to address the integration in `GetIntegration`, `UpdateIntegration`, `DeleteIntegration`, and the enable / disable / refresh / health RPCs. |
| title | [ string](#string) | Human-friendly name shown in the Coalesce Quality UI. Set by you on create and editable via update. |
| disabled | [ bool](#bool) | Whether the integration is currently disabled. A disabled integration keeps its configuration but is not scheduled or refreshed. Toggle with `EnableIntegration` / `DisableIntegration`. |
| created\_at | [ Timestamp](#timestamp) | When the integration was created. |
| updated\_at | [ Timestamp](#timestamp) | When the integration was last modified (configuration, title, or enabled / disabled state). |
| etag | [ string](#string) | Opaque entity tag for optimistic concurrency. Pass it back as `UpdateIntegrationRequest.etag` / `DeleteIntegrationRequest.etag` to ensure you are modifying the version you last read; a mismatch is rejected with a 409 / ABORTED. The value is weak (prefixed `W/`) and opaque — do not parse it. |
| platform | [ DataPlatformType](#dataplatformtype) | The data platform this integration connects to (for example POSTGRES, CLICKHOUSE, SNOWFLAKE). UNSPECIFIED for integrations that are not data warehouses or databases. |
| dialect | [ SqlDialect](#sqldialect) | The SQL dialect this integration understands — useful when writing SQL against it, such as custom monitors or reconciliation queries. Determined by the platform. UNSPECIFIED for integrations that are not data warehouses or databases. |
| config | [ IntegrationConfig](#integrationconfig) | Connection configuration. Secret fields are masked (empty) on reads. |
| outputs | [ IntegrationOutputs](#integrationoutputs) | Server-generated outputs (read-only). Empty for types that produce none. |
| capabilities | [ Capabilities](#capabilities) | Actions valid for this integration in its current state (read-only hint). |
### IntegrationConfig
(synq.integrations.v1.IntegrationConfig)
IntegrationConfig is the connection configuration for an integration.
Exactly one variant of the `config` oneof is set; the populated variant
determines the integration's type. The config holds the connection details
(host, credentials, scope) for the connected system.
Secret fields (passwords, tokens, keys) follow write-only semantics: they are
masked (returned empty) on reads. See each `*Conf` message and
`IntegrationsService.UpdateIntegration` for preserve / rotate / clear rules.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.bigquery | [ BigQueryCloudConf](#bigquerycloudconf) | Google BigQuery connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.snowflake | [ SnowflakeCloudConf](#snowflakecloudconf) | Snowflake connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.redshift | [ RedshiftCloudConf](#redshiftcloudconf) | Amazon Redshift connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.postgres | [ PostgresCloudConf](#postgrescloudconf) | PostgreSQL connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.mysql | [ MySQLCloudConf](#mysqlcloudconf) | MySQL connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.clickhouse | [ ClickhouseCloudConf](#clickhousecloudconf) | ClickHouse connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.databricks | [ DatabricksCloudConf](#databrickscloudconf) | Databricks connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.trino | [ TrinoCloudConf](#trinocloudconf) | Trino / Presto connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.mssql | [ MSSQLCloudConf](#mssqlcloudconf) | Microsoft SQL Server / Azure SQL connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.oracle | [ OracleCloudConf](#oraclecloudconf) | Oracle Database connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.duckdb | [ DuckDBCloudConf](#duckdbcloudconf) | DuckDB / MotherDuck connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.athena | [ AthenaCloudConf](#athenacloudconf) | Amazon Athena connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.dbt\_cloud | [ DbtCloudConf](#dbtcloudconf) | dbt Cloud connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.fabric | [ FabricCloudConf](#fabriccloudconf) | Microsoft Fabric connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.db2 | [ Db2CloudConf](#db2cloudconf) | IBM Db2 for Linux, UNIX and Windows connection. |
### IntegrationOutputs
(synq.integrations.v1.IntegrationOutputs)
Generated, non-secret values produced by the server when an integration is
created (for example a service-account email to grant access to, or a public
key to install). Outputs are read-only and are never accepted on writes.
At most one variant is set, matching the integration's type. Types that
produce no outputs leave this message empty.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) outputs.bigquery | [ BigQueryOutputs](#bigqueryoutputs) | BigQuery: the service-account identity to grant dataset access to. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) outputs.snowflake | [ SnowflakeOutputs](#snowflakeoutputs) | Snowflake: the public key to register when using key-pair auth. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) outputs.athena | [ AthenaOutputs](#athenaoutputs) | Athena: the resolved AWS caller identity. |
### SnowflakeOutputs
(synq.integrations.v1.SnowflakeOutputs)
Snowflake generated outputs.
| Field | Type | Description |
| - | - | - |
| public\_key | [ string](#string) | Public key (PEM) to register on the Snowflake user when authenticating with a Coalesce Quality–managed key pair. Empty when you supply your own credentials. |
### ClickhouseCloudConf
(synq.integrations.v1.ClickhouseCloudConf)
ClickHouse cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Host address or IP. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional string](#string) | Port (e.g. "8123" for HTTP, "9440" for HTTPS). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_username.username | [optional string](#string) | Username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_default\_dataset.default\_dataset | [optional string](#string) | Database the connection opens with, so unqualified table names in a query resolve against it. It is not a scrape filter: metadata is read from system tables and covers every database the credentials can see, whatever this says. |
| additional\_parameters | [map AdditionalParametersEntry](#additionalparametersentry) | Additional connection parameters as key/value pairs (e.g. "max\_execution\_time": "30"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ssl.ssl | [optional bool](#bool) | Enable SSL/TLS. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database\_name.database\_name | [optional string](#string) | Name this ClickHouse is published under, and the top element of every path and breadcrumb its tables appear in — `prod`, `staging`, `eu-analytics`. ClickHouse has no container above a database, so something has to name the service; left empty that is the connection host, which is correct but unreadable for a ClickHouse Cloud endpoint. It is not a database and does not restrict what is scraped. Changing it republishes every table under new paths, so pick one per service and keep it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs from system.query\_log. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cluster.cluster | [optional ClickhouseClusterConf](#clickhouseclusterconf) | How ClickHouse system tables are read. Omit to keep reading across a cluster named "default", which every ClickHouse Cloud service provides. |
### ClickhouseCloudConf.AdditionalParametersEntry
(synq.integrations.v1.ClickhouseCloudConf.AdditionalParametersEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### ClickhouseClusterConf
(synq.integrations.v1.ClickhouseClusterConf)
How metadata reads address ClickHouse system tables.
System tables are per-node, so on a service with more than one replica a plain
read reflects whichever replica answered rather than the whole warehouse.
| Field | Type | Description |
| - | - | - |
| mode | [ ClickhouseClusterMode](#clickhouseclustermode) | none |
| name | [ string](#string) | Cluster to read through, as named under remote\_servers in the ClickHouse configuration. `SELECT DISTINCT cluster FROM system.clusters` lists what a server has. Empty means "default". Ignored in single-node mode. |
### BigQueryCloudConf
(synq.integrations.v1.BigQueryCloudConf)
Google BigQuery cloud-managed connection.
All fields are optional so an update can carry only what changes: omit a field
to leave it unchanged, or send a new value to set it. Secret fields are
write-only (masked, returned empty, on reads); omit to keep the stored value
or send a new value to rotate it.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_project\_id.project\_id | [optional string](#string) | Google Cloud project id containing the datasets to monitor. |
| blacklisted\_datasets | [repeated string](#string) | Dataset name patterns to exclude from ingestion. Mutually exclusive with `datasets` — when `datasets` is set, this is ignored. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_service\_account\_key.service\_account\_key | [optional string](#string) | Service-account key JSON. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_region.region | [optional string](#string) | Google Cloud region for the project (e.g. "us-central1", "EU"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest BigQuery query logs from INFORMATION\_SCHEMA. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs (e.g. "project.dataset.table"). |
| datasets | [repeated string](#string) | Explicit list of datasets to scrape. When set, only these are queried (no project-level dataset listing needed). When empty, all visible datasets are discovered. Mutually exclusive with `blacklisted_datasets`. |
### AthenaAccessKeyAuth
(synq.integrations.v1.AthenaAccessKeyAuth)
Amazon Athena access-key authentication (long-lived IAM user credentials).
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_access\_key\_id.access\_key\_id | [optional string](#string) | AWS access key id (e.g. "AKIA..."). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_secret\_access\_key.secret\_access\_key | [optional string](#string) | AWS secret access key. Write-only — masked on reads. |
### AthenaCloudConf
(synq.integrations.v1.AthenaCloudConf)
Amazon Athena cloud-managed connection. Athena is serverless — there is no
host/port; the endpoint is implied by the AWS region.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_region.region | [optional string](#string) | AWS region hosting Athena and the Glue Data Catalog (e.g. "eu-central-1"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_workgroup.workgroup | [optional string](#string) | Workgroup to run queries under. Defaults to "primary" when empty; must have a query result location configured. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.access\_key | [ AthenaAccessKeyAuth](#athenaaccesskeyauth) | Long-lived IAM user access key. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_show\_create\_table.use\_show\_create\_table | [optional bool](#bool) | Use SHOW CREATE TABLE per object to retrieve full table DDL. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_show\_create\_view\.use\_show\_create\_view | [optional bool](#bool) | Use SHOW CREATE VIEW per view to retrieve full view DDL. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Fetch Athena query history (management API calls; no per-scan billing). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_iceberg\_metrics\_scan.use\_iceberg\_metrics\_scan | [optional bool](#bool) | For Iceberg tables, scan metadata tables for row counts and file sizes (one Athena query per table). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scope.scope | [optional ScopeFilter](#scopefilter) | Include/exclude scope filter. ScopeRule.database = Glue catalog (usually "AwsDataCatalog"), ScopeRule.schema = Glue database, ScopeRule.table = table/view. |
### RedshiftCloudConf
(synq.integrations.v1.RedshiftCloudConf)
Amazon Redshift cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | Username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Cluster endpoint hostname. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Port (typically 5439). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Database to connect to and ingest metadata from. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs from system views. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_freshness\_from\_query\_logs.freshness\_from\_query\_logs | [optional bool](#bool) | Estimate table freshness from query logs (SYS\_QUERY\_DETAIL). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Table name for storing SQL test audit logs (e.g., 'sql\_test\_audit'). Auto-creation quotes the value as a single identifier, so a schema-qualified name only works against a table you have created yourself. |
### DuckDBCloudConf
(synq.integrations.v1.DuckDBCloudConf)
DuckDB (via MotherDuck) cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_motherduck\_account.motherduck\_account | [optional string](#string) | MotherDuck account name or identifier. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_access\_token.access\_token | [optional string](#string) | MotherDuck access token. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
### MySQLCloudConf
(synq.integrations.v1.MySQLCloudConf)
MySQL cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | Username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Password. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Host address or IP. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Port (typically 3306). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
### TrinoBasicAuth
(synq.integrations.v1.TrinoBasicAuth)
Trino basic (username / password) authentication.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | Trino username. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Trino password. Write-only — masked on reads. |
### TrinoCloudConf
(synq.integrations.v1.TrinoCloudConf)
Trino / Presto cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Coordinator hostname or IP. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Port (leave unset for the driver default — typically 443 for HTTPS, 8080 for HTTP). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.basic | [ TrinoBasicAuth](#trinobasicauth) | Basic username/password auth. |
| catalogs | [repeated string](#string) | Catalogs to ingest metadata from (e.g. \["hive", "postgresql"]). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Ingest query logs from system tables. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_no\_show\_create\_view\.no\_show\_create\_view | [optional bool](#bool) | Disable SHOW CREATE VIEW for view DDL. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_no\_show\_create\_table.no\_show\_create\_table | [optional bool](#bool) | Disable SHOW CREATE TABLE for table DDL. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_no\_materialized\_views.no\_materialized\_views | [optional bool](#bool) | Disable fetching materialized views from system.metadata.materialized\_views. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_table\_comments.fetch\_table\_comments | [optional bool](#bool) | Fetch table comments from system.metadata.table\_comments. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_plaintext.use\_plaintext | [optional bool](#bool) | Use plaintext HTTP instead of HTTPS. Only enable when SSL is unsupported or the connection is otherwise secured (e.g. a VPN). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully-qualified table for storing SQL test audit logs. |
### DbtCloudConf
(synq.integrations.v1.DbtCloudConf)
dbt Cloud transformation orchestration cloud-managed connection.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_account\_id.account\_id | [optional string](#string) | dbt Cloud account id (the numeric id from your dbt Cloud URL). |
| job\_ids | [repeated string](#string) | dbt Cloud jobs to track, by job id. Provide the full set on every update (the list is replaced, not merged). When empty, all jobs visible to the API token are tracked. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_token.token | [optional string](#string) | dbt Cloud API token (service token recommended). Write-only — masked on reads; omit to keep the stored token, send a new value to rotate it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_project\_id.project\_id | [optional string](#string) | Project id used to generate asset identifiers. Use the same id for multiple projects that should be merged. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_api\_endpoint.api\_endpoint | [optional string](#string) | dbt Cloud API hostname without scheme (e.g. "cloud.getdbt.com", "emea.dbt.com"). Empty uses the multi-tenant default. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_default\_database\_instance.default\_database\_instance | [optional string](#string) | Database instance identifier, required where database names are not globally unique (e.g. a Snowflake account). |
### FabricCloudConf
(synq.integrations.v1.FabricCloudConf)
Microsoft Fabric cloud-managed connection.
Fabric is workspace-scoped with cross-database queries (like Snowflake or
Databricks, not a single database): the workspace SQL analytics endpoint
exposes every warehouse / lakehouse in the workspace as a database, queryable
via three-part \[database].\[schema].\[table] T-SQL names.
Authentication is Microsoft Entra ID only — Fabric rejects SQL logins, so the
headless identity is an Entra service principal (application id + secret), or
a pre-acquired Entra access token.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). Secret fields are write-only (masked on reads); omit to keep or
send a new value to rotate.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_host.host | [optional string](#string) | Workspace SQL analytics endpoint host, e.g. "\.datawarehouse.fabric.microsoft.com". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Default execution database — the context in which unqualified ad-hoc / monitor SQL resolves. Defaults to "master" (the always-present workspace entry point) when empty. Metadata reads and generated metrics SQL are fully database-qualified, so this only affects unqualified queries. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.service\_principal | [ FabricServicePrincipalAuth](#fabricserviceprincipalauth) | Entra service principal (application id + secret). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_access\_token.access\_token | [optional string](#string) | Pre-acquired Entra OAuth access token for the SQL scope ([https://database.windows.net/.default](https://database.windows.net/.default)). When set, takes precedence over the service principal — for callers that mint their own token out of band (managed identity / workload-identity federation). Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Fetch Fabric query history for cost attribution and SQL / column-level lineage. Management-plane reads only, no per-query billing. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scope.scope | [optional ScopeFilter](#scopefilter) | Include/exclude scope filter. ScopeRule.database = Fabric database / warehouse, ScopeRule.schema = schema, ScopeRule.table = table / view. |
### FabricServicePrincipalAuth
(synq.integrations.v1.FabricServicePrincipalAuth)
Microsoft Fabric Entra service-principal credentials.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_client\_id.client\_id | [optional string](#string) | Entra application (client) ID of the service principal. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_client\_secret.client\_secret | [optional string](#string) | Service principal client secret. Write-only — masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_tenant\_id.tenant\_id | [optional string](#string) | Entra tenant (directory) ID. Optional: inferred from the Fabric endpoint when empty; set it when the authority is not the service principal's home tenant. |
### BatchGetIntegrationsRequest
(synq.integrations.v1.BatchGetIntegrationsRequest)
Request to fetch multiple integrations by id.
| Field | Type | Description |
| - | - | - |
| integration\_ids | [repeated string](#string) | Integration ids (UUIDs). Unknown ids are omitted from the response. |
### BatchGetIntegrationsResponse
(synq.integrations.v1.BatchGetIntegrationsResponse)
Response with the requested integrations.
| Field | Type | Description |
| - | - | - |
| integrations | [map IntegrationsEntry](#integrationsentry) | The integrations that were found, keyed by integration id. Requested ids that do not exist are absent from the map. |
### BatchGetIntegrationsResponse.IntegrationsEntry
(synq.integrations.v1.BatchGetIntegrationsResponse.IntegrationsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Integration](#integration) | none |
### CreateIntegrationRequest
(synq.integrations.v1.CreateIntegrationRequest)
Request to create an integration.
| Field | Type | Description |
| - | - | - |
| title | [ string](#string) | Human-friendly name for the integration. |
| config | [ IntegrationConfig](#integrationconfig) | Connection configuration. Exactly one `config` variant must be set; it fixes the integration's type. |
### CreateIntegrationResponse
(synq.integrations.v1.CreateIntegrationResponse)
Response from creating an integration.
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | The created integration, including its assigned id, etag, and any generated outputs. |
### DeleteIntegrationRequest
(synq.integrations.v1.DeleteIntegrationRequest)
Request to delete an integration.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration to delete (UUID). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard (see `UpdateIntegrationRequest.etag`). |
### DeleteIntegrationResponse
(synq.integrations.v1.DeleteIntegrationResponse)
Response from deleting an integration.
### DisableIntegrationRequest
(synq.integrations.v1.DisableIntegrationRequest)
Request to disable an integration.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration to disable (UUID). |
### DisableIntegrationResponse
(synq.integrations.v1.DisableIntegrationResponse)
Response from disabling an integration.
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | The integration after disabling. |
### EnableIntegrationRequest
(synq.integrations.v1.EnableIntegrationRequest)
Request to enable an integration.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration to enable (UUID). |
### EnableIntegrationResponse
(synq.integrations.v1.EnableIntegrationResponse)
Response from enabling an integration.
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | The integration after enabling. |
### GetIntegrationHealthRequest
(synq.integrations.v1.GetIntegrationHealthRequest)
Request for an integration's health and run history.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration (UUID). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Cursor-based pagination over the run history. Omit for a bounded recent window (most recent runs from the last 7 days). |
### GetIntegrationHealthResponse
(synq.integrations.v1.GetIntegrationHealthResponse)
Response with current health plus a page of recent runs.
| Field | Type | Description |
| - | - | - |
| health | [ IntegrationHealth](#integrationhealth) | Current health summary. |
| runs | [repeated IntegrationRun](#integrationrun) | Recent runs, most recent first. |
| page\_info | [ PageInfo](#pageinfo) | Pagination metadata; pass `page_info.last_id` as the next cursor. |
### GetIntegrationRequest
(synq.integrations.v1.GetIntegrationRequest)
Request to fetch a single integration.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Integration id (UUID). |
### GetIntegrationResponse
(synq.integrations.v1.GetIntegrationResponse)
Response with a single integration.
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | The requested integration. |
### IntegrationHealth
(synq.integrations.v1.IntegrationHealth)
Current health summary for an integration.
| Field | Type | Description |
| - | - | - |
| status | [ HealthStatus](#healthstatus) | Status derived from the most recent run. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_run\_at.last\_run\_at | [optional Timestamp](#timestamp) | When the most recent run occurred. Unset when no runs are observed. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_success\_at.last\_success\_at | [optional Timestamp](#timestamp) | When the most recent successful run occurred. Unset when none is observed. |
| message | [ string](#string) | Human-readable message from the most recent run (for example an error summary). Empty when there is nothing to report. |
| healthy | [ bool](#bool) | Convenience flag: true when `status` is OK and the last run is recent relative to the integration's expected refresh cadence. |
### IntegrationRun
(synq.integrations.v1.IntegrationRun)
A single observed run (refresh / sync) of an integration.
| Field | Type | Description |
| - | - | - |
| run\_id | [ string](#string) | Run identifier. |
| status | [ HealthStatus](#healthstatus) | Outcome of this run. |
| started\_at | [ Timestamp](#timestamp) | When the run started. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_finished\_at.finished\_at | [optional Timestamp](#timestamp) | When the run finished. Unset while still running. |
| message | [ string](#string) | Human-readable message for this run (for example an error summary). Empty when there is nothing to report. |
### ListIntegrationsRequest
(synq.integrations.v1.ListIntegrationsRequest)
Request to list integrations.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_disabled.disabled | [optional bool](#bool) | Optional filter: when set, return only enabled (false) or only disabled (true) integrations. Unset means both. |
### ListIntegrationsResponse
(synq.integrations.v1.ListIntegrationsResponse)
Response with the workspace's integrations.
| Field | Type | Description |
| - | - | - |
| integrations | [repeated Integration](#integration) | All integrations matching the request. |
### RefreshIntegrationRequest
(synq.integrations.v1.RefreshIntegrationRequest)
Request to trigger an ad-hoc refresh.
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration to refresh (UUID). |
### RefreshIntegrationResponse
(synq.integrations.v1.RefreshIntegrationResponse)
Response from triggering a refresh.
### UpdateIntegrationRequest
(synq.integrations.v1.UpdateIntegrationRequest)
Request to update an integration. The `config` is replaced wholesale; `title`
is patch-style (omit to leave unchanged).
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Id of the integration to update (UUID). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | New human-friendly name. Omit to leave the title unchanged. |
| config | [ IntegrationConfig](#integrationconfig) | New connection configuration. The populated `config` variant must match the existing integration's type. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the update is applied only if it matches the integration's current etag; otherwise ABORTED (HTTP 409). Omit for last-write-wins. |
### UpdateIntegrationResponse
(synq.integrations.v1.UpdateIntegrationResponse)
Response from updating an integration.
| Field | Type | Description |
| - | - | - |
| integration | [ Integration](#integration) | The updated integration with a new etag. |
### Db2CloudConf
(synq.integrations.v1.Db2CloudConf)
IBM Db2 for Linux, UNIX and Windows (Db2 LUW) connection. Each integration
opens one database, the one you would name in `CONNECT TO `. An
instance can hold several databases, each with its own SYSCAT catalog views,
so add an integration for every database you want to monitor.
The keyword in parentheses after each field is the matching keyword of a Db2
CLI connection string, db2cli.ini or db2dsdriver.cfg, and values are the
same, so you can copy them from a data source you already use. Db2 for z/OS
and Db2 for i are not supported.
Fields are optional so an update can carry only what changes (omit = leave
unchanged). The password is write-only (masked on reads); omit it to keep the
stored one or send a new value to rotate it.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_hostname.hostname | [optional string](#string) | Host name or IP address of the Db2 server (HOSTNAME). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | TCP/IP port of the instance (PORT): the value of the SVCENAME database manager configuration parameter, or SSL\_SVCENAME when security is "SSL" (`db2 get dbm cfg`). Defaults to 50000, or 50001 when security is "SSL". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Database name or alias (DATABASE), as in `CONNECT TO `. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | Authorization ID to connect with (UID). Db2 checks it through the instance's security plugin: against the server's operating system by default, or against LDAP when an LDAP plugin is configured. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_password.password | [optional string](#string) | Password of the authorization ID (PWD). Write-only, masked on reads. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_security.security | [optional string](#string) | Set to "SSL" to connect over SSL/TLS (SECURITY=SSL). The instance needs SSL\_SVCENAME set and SSL listed in DB2COMM. Leave empty for a plain TCP/IP connection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ssl\_server\_certificate\_pem.ssl\_server\_certificate\_pem | [optional string](#string) | The server's certificate, or the certificate of the CA that signed it, in PEM format: the text starting with "-----BEGIN CERTIFICATE-----". The .arm file you get from the instance's keystore with `gsk8capicmd_64 -cert -extract` holds exactly this. Needs security set to "SSL". Leave empty to check the server against the public certificate authorities. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_authentication.authentication | [optional string](#string) | How the authorization ID and password travel to the server (AUTHENTICATION). The values match the database manager configuration parameter of the same name: - "SERVER" (default): sent unencrypted. Use it with security "SSL" on an untrusted network. - "SERVER\_ENCRYPT": sent encrypted. The instance's AUTHENTICATION must accept encrypted credentials, for example SERVER\_ENCRYPT. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fetch\_query\_logs.fetch\_query\_logs | [optional bool](#bool) | Read query history from the package cache (MON\_GET\_PKG\_CACHE\_STMT). The authorization ID needs EXECUTE on that table function. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_audit\_table\_fqn.audit\_table\_fqn | [optional string](#string) | Fully qualified table for storing SQL test audit logs. |
### ScopeAuthorization
(synq.v1.ScopeAuthorization)
| Field | Type | Description |
| - | - | - |
| scopes | [repeated Scope](#scope) | none |
### PageInfo
(synq.v1.PageInfo)
| Field | Type | Description |
| - | - | - |
| total\_count | [ int32](#int32) | Total number of items matching the request. May not be available for cursor-based pagination where the total is unknown or expensive to compute. Deprecated: most of the APIs which require pagination don't know the total count. |
| count | [ int32](#int32) | Number of items returned in this response. |
| last\_id | [ string](#string) | Opaque cursor token for fetching the next page of results. Pass this value as the cursor in the next Pagination request to continue pagination. Empty if there are no more pages. |
### Pagination
(synq.v1.Pagination)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cursor.cursor | [optional string](#string) | Opaque cursor token for the next page of results. If not provided, returns the first page. This is an encoded token that should be passed as-is from the previous PageInfo.last\_id. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_page\_size.page\_size | [optional int32](#int32) | Maximum number of items to return in a single page. If not provided, defaults vary per API. |
### OAuthAuthentication
(synq.platforms.v1.OAuthAuthentication)
Authentication using OAuth credentials
| Field | Type | Description |
| - | - | - |
| client\_id | [ string](#string) | none |
| client\_secret | [ string](#string) | none |
### PasswordAuthentication
(synq.platforms.v1.PasswordAuthentication)
Authentication using a username and password
| Field | Type | Description |
| - | - | - |
| username | [ string](#string) | none |
| password | [ string](#string) | none |
### PlatformAuthentication
(synq.platforms.v1.PlatformAuthentication)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.token | [ TokenAuthentication](#tokenauthentication) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.oauth | [ OAuthAuthentication](#oauthauthentication) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.password | [ PasswordAuthentication](#passwordauthentication) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.synq\_managed\_key\_pair | [ SynqManagedKeyPairAuthentication](#synqmanagedkeypairauthentication) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) authentication.user\_managed\_key\_pair | [ UserManagedKeyPairAuthentication](#usermanagedkeypairauthentication) | none |
### SynqManagedKeyPairAuthentication
(synq.platforms.v1.SynqManagedKeyPairAuthentication)
Authentication using a managed key pair.
The public key will be set and returned by the server after the authentication is created.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_public\_key.public\_key | [optional string](#string) | none |
### TokenAuthentication
(synq.platforms.v1.TokenAuthentication)
Authentication using a token (API key, PAT, etc)
| Field | Type | Description |
| - | - | - |
| token | [ string](#string) | none |
### UserManagedKeyPairAuthentication
(synq.platforms.v1.UserManagedKeyPairAuthentication)
Authentication using a user-managed key pair.
The private key is stored safely as a secret. The response does not include the private key for security.
| Field | Type | Description |
| - | - | - |
| private\_key | [ string](#string) | none |
### SetPlatformAuthenticationRequest
(synq.platforms.v1.SetPlatformAuthenticationRequest)
| Field | Type | Description |
| - | - | - |
| platform | [ DataPlatformIdentifier](#dataplatformidentifier) | Platform identifier |
| authentication | [ PlatformAuthentication](#platformauthentication) | Authentication details |
### SetPlatformAuthenticationResponse
(synq.platforms.v1.SetPlatformAuthenticationResponse)
| Field | Type | Description |
| - | - | - |
| success | [ bool](#bool) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_authentication.authentication | [optional PlatformAuthentication](#platformauthentication) | The authentication is returned in case there is a server side set information to be passed to the client. |
### AthenaIdentifier
(synq.platforms.v1.AthenaIdentifier)
| Field | Type | Description |
| - | - | - |
| instance | [ string](#string) | AWS account and region the Athena workgroup runs in, formatted as "\.\" (e.g. "123456789012.eu-west-1"). Auto-derived by SYNQ from sts:GetCallerIdentity at integration setup time. |
| catalog | [ string](#string) | Glue Data Catalog name. Almost always "AwsDataCatalog" — the default catalog for Athena unless the customer registered a federated catalog. |
### BigqueryIdentifier
(synq.platforms.v1.BigqueryIdentifier)
| Field | Type | Description |
| - | - | - |
| project | [ string](#string) | BigQuery project |
### ClickhouseIdentifier
(synq.platforms.v1.ClickhouseIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Clickhouse host inclusive of port |
| schema | [ string](#string) | Clickhouse database |
### DataPlatformIdentifier
(synq.platforms.v1.DataPlatformIdentifier)
DataPlatformIdentifier is a unique reference to a data platform integrated with Coalesce Quality.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.unknown | [ UnknownDataPlatform](#unknowndataplatform) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.bigquery | [ BigqueryIdentifier](#bigqueryidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.clickhouse | [ ClickhouseIdentifier](#clickhouseidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.snowflake | [ SnowflakeIdentifier](#snowflakeidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.redshift | [ RedshiftIdentifier](#redshiftidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.postgres | [ PostgresIdentifier](#postgresidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.mysql | [ MysqlIdentifier](#mysqlidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.databricks | [ DatabricksIdentifier](#databricksidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.dbt\_cloud | [ DbtCloudIdentifier](#dbtcloudidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.sql\_mesh | [ SqlMeshIdentifier](#sqlmeshidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.duckdb | [ DuckDbIdentifier](#duckdbidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.trino | [ TrinoIdentifier](#trinoidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.mssql | [ MssqlIdentifier](#mssqlidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.oracle | [ OracleIdentifier](#oracleidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.athena | [ AthenaIdentifier](#athenaidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.fabric | [ FabricIdentifier](#fabricidentifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.db2 | [ Db2Identifier](#db2identifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.synq\_integration\_id | [ string](#string) | Coalesce Quality integration ID maps to the created integration on the Coalesce Quality platform. |
### DatabricksIdentifier
(synq.platforms.v1.DatabricksIdentifier)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | URL of the databricks workspace |
### Db2Identifier
(synq.platforms.v1.Db2Identifier)
IBM Db2 for Linux, UNIX and Windows. An integration connects to one
database, so host and database together identify it.
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host name of the Db2 server |
| database | [ string](#string) | Database name or alias, as in `CONNECT TO ` |
### DbtCloudIdentifier
(synq.platforms.v1.DbtCloudIdentifier)
| Field | Type | Description |
| - | - | - |
| api\_endpoint | [ string](#string) | API endpoint for Dbt Cloud |
| account\_id | [ string](#string) | Account ID |
| project\_id | [ string](#string) | Project ID |
### DuckDbIdentifier
(synq.platforms.v1.DuckDbIdentifier)
| Field | Type | Description |
| - | - | - |
| motherduck\_account | [ string](#string) | none |
### FabricIdentifier
(synq.platforms.v1.FabricIdentifier)
| Field | Type | Description |
| - | - | - |
| instance | [ string](#string) | Workspace SQL analytics endpoint host, e.g. "\.datawarehouse.fabric.microsoft.com". Identifies the Fabric workspace the integration connects to. Fabric is workspace-scoped with cross-database queries, so the workspace host alone identifies the platform (like Databricks' workspace URL); a specific warehouse/table is addressed by FabricTableIdentifier. |
| workspace\_id | [ string](#string) | Fabric workspace GUID — the identifier used by the Fabric REST API for workspace-scoped resources. The Fabric endpoint host in `instance` encodes the tenant and workspace GUIDs; this is the decoded workspace GUID, provided so you don't have to decode it yourself. `instance` remains the identifying value. Optional: empty when the host isn't a standard Fabric endpoint. |
| tenant\_id | [ string](#string) | Entra (Azure AD) tenant GUID, decoded from the endpoint host in `instance` alongside workspace\_id. Optional: empty when the host isn't a standard Fabric endpoint. |
### MssqlIdentifier
(synq.platforms.v1.MssqlIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | SQL Server host |
| database | [ string](#string) | Database name |
### MysqlIdentifier
(synq.platforms.v1.MysqlIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Mysql host inclusive of port |
### OracleIdentifier
(synq.platforms.v1.OracleIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Oracle host |
| service\_name | [ string](#string) | Oracle service name (PDB) |
### PostgresIdentifier
(synq.platforms.v1.PostgresIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Postgres host inclusive of port |
| database | [ string](#string) | Postgres database |
### RedshiftIdentifier
(synq.platforms.v1.RedshiftIdentifier)
| Field | Type | Description |
| - | - | - |
| cluster | [ string](#string) | Redshift cluster |
| database | [ string](#string) | Redshift database |
### SnowflakeIdentifier
(synq.platforms.v1.SnowflakeIdentifier)
| Field | Type | Description |
| - | - | - |
| account | [ string](#string) | Snowflake account |
| database | [ string](#string) | Snowflake database |
### SqlMeshIdentifier
(synq.platforms.v1.SqlMeshIdentifier)
| Field | Type | Description |
| - | - | - |
| default\_database\_instance | [ string](#string) | Default database instance for SQL Mesh |
### TrinoIdentifier
(synq.platforms.v1.TrinoIdentifier)
| Field | Type | Description |
| - | - | - |
| coordinator | [ string](#string) | none |
### UnknownDataPlatform
(synq.platforms.v1.UnknownDataPlatform)
### Event
(synq.webhooks.v1.Event)
A webhook event delivered to a customer-configured endpoint.
Payload signing: every outgoing request is signed so the receiver can verify
it originated from Coalesce Quality and was not tampered with or replayed. Two
headers are sent on every delivery:
X-Coalesce-Timestamp: \
X-Coalesce-Signature: v1=\
The signature is computed as:
signed\_payload = "\{X-Coalesce-Timestamp}." + \
signature = hex( HMAC\_SHA256(key = signing\_secret, msg = signed\_payload) )
where signing\_secret is the per-integration secret shown (read-only) in the
webhook integration settings. To verify:
1. Read X-Coalesce-Timestamp and the raw request body.
2. Recompute the signature with your copy of the signing secret and compare
it against the v1=... value in X-Coalesce-Signature using a constant-time
comparison.
3. Optionally reject deliveries whose timestamp is outside your tolerance
window (retries reuse the original signing timestamp, so allow for the
retry backoff window — up to \~30 minutes).
The v1= prefix identifies the signature scheme; future schemes may add
additional space-separated values to the header.
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | none |
| event\_id | [ string](#string) | none |
| event\_time | [ Timestamp](#timestamp) | none |
| event\_type | [ EventType](#eventtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.ping | [ Ping](#ping) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.issue\_created | [ IssueCreated](#issuecreated) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.issue\_updated | [ IssueUpdated](#issueupdated) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.issue\_status\_updated | [ IssueStatusUpdated](#issuestatusupdated) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.issue\_closed | [ IssueClosed](#issueclosed) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.incident\_open | [ IncidentOpen](#incidentopen) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.incident\_closed | [ IncidentClosed](#incidentclosed) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.incident\_cancelled | [ IncidentCancelled](#incidentcancelled) | none |
| callbacks | [repeated Callback](#callback) | none |
### IncidentCancelled
(synq.webhooks.v1.IncidentCancelled)
| Field | Type | Description |
| - | - | - |
| incident | [ IncidentSummary](#incidentsummary) | none |
### IncidentClosed
(synq.webhooks.v1.IncidentClosed)
| Field | Type | Description |
| - | - | - |
| incident | [ IncidentSummary](#incidentsummary) | none |
### IncidentOpen
(synq.webhooks.v1.IncidentOpen)
| Field | Type | Description |
| - | - | - |
| incident | [ IncidentSummary](#incidentsummary) | none |
### IncidentSummary
(synq.webhooks.v1.IncidentSummary)
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | none |
| incident\_url | [ string](#string) | none |
| title | [ string](#string) | none |
| description | [ string](#string) | none |
| description\_html | [ string](#string) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ended\_at.ended\_at | [optional Timestamp](#timestamp) | none |
### IssueClosed
(synq.webhooks.v1.IssueClosed)
| Field | Type | Description |
| - | - | - |
| issue | [ IssueSummary](#issuesummary) | none |
### IssueCreated
(synq.webhooks.v1.IssueCreated)
| Field | Type | Description |
| - | - | - |
| issue | [ IssueSummary](#issuesummary) | none |
### IssueStatusUpdated
(synq.webhooks.v1.IssueStatusUpdated)
| Field | Type | Description |
| - | - | - |
| issue | [ IssueSummary](#issuesummary) | none |
### IssueSummary
(synq.webhooks.v1.IssueSummary)
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | none |
| issue\_group\_id | [ string](#string) | none |
| issue\_url | [ string](#string) | none |
| title | [ string](#string) | Summary of the issue, what happened and where. |
| description | [ string](#string) | Detailed description of the issue. In the Markdown format. |
| description\_html | [ string](#string) | Detailed description of the issue. In the HTML format. |
| trigger\_entity | [ IssueEntity](#issueentity) | Entity which triggered the issue. |
| directly\_affected\_entities | [repeated IssueEntity](#issueentity) | Entities directly affected by the issue, not considering downstream ones. |
| monitored\_entities | [repeated IssueEntity](#issueentity) | The primary entities being monitored that are associated with this issue, for example the dbt model or dbt Cloud job that a failing test is attached to. |
| started\_at | [ Timestamp](#timestamp) | Time when the issue was triggered. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ended\_at.ended\_at | [optional Timestamp](#timestamp) | Time when the issue was closed. |
| trigger\_run\_id | [ string](#string) | none |
| trigger\_name | [ string](#string) | none |
| trigger\_message | [ string](#string) | none |
| status | [ IssueStatus](#issuestatus) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_status\_actor.status\_actor | [optional Actor](#actor) | The user who set the current status, when the transition was human-initiated (e.g. someone pressing Expected / Fixed / No action needed / Investigating in the app or via the public API). This is the same actor exposed by the Issues API as `Issue.latest_status_actor`, so the two feeds can be joined on it. |
Set only when an actor is known: it is populated on `issue_status_updated` events and left unset for system-driven transitions with no human actor (e.g. a status implied by a monitor run). Consumers must treat it as optional and tolerate its absence.
Carries the strongest identity the caller proved — a Coalesce Quality email, Slack user id, or PagerDuty user id — alongside a display `name`. |
\| owners | [repeated Owner](#owner) | Owners of the entities this issue is about.
Covers only the direct owners of the affected entities, not the owners of downstream entities. Empty when the affected entities have no owner assigned. Ordered as shown in the app. |
### IssueSummary.IssueEntity
(synq.webhooks.v1.IssueSummary.IssueEntity)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| type\_name | [ string](#string) | none |
| identifier | [ Identifier](#identifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_folder.folder | [optional string](#string) | none |
| entity\_url | [ string](#string) | none |
### IssueSummary.Owner
(synq.webhooks.v1.IssueSummary.Owner)
An owner (person or team) responsible for an entity, as assigned in
Coalesce Quality (natively or synced from a catalog).
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Identifier of the owner within the workspace. Matches the id on the Owners API, so callers can fetch the full owner record (contacts, source) from that API. |
| title | [ string](#string) | Display name of the owner, e.g. a team or person name. |
| url | [ string](#string) | Deep link to the owner in the Coalesce Quality app. |
### IssueUpdated
(synq.webhooks.v1.IssueUpdated)
| Field | Type | Description |
| - | - | - |
| issue | [ IssueSummary](#issuesummary) | none |
### Ping
(synq.webhooks.v1.Ping)
Test event sent during a webhook setup.
| Field | Type | Description |
| - | - | - |
| message | [ string](#string) | none |
### Callback
(synq.webhooks.v1.Callback)
| Field | Type | Description |
| - | - | - |
| url | [ string](#string) | none |
| action\_name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.issues\_command | [ IssuesCommand](#issuescommand) | none |
### FlatEvent
(synq.webhooks.v1.FlatEvent)
A webhook event in the flat format — an experimental, opt-in alternative to
`Event` that carries the same information in a flatter shape.
Instead of nesting the payload under a per-event-type field (`issue_created`,
`issue_updated`, `incident_open`, …), the payload is carried by a single
top-level field — `issue`, `incident`, or `ping` — and `event_type` says
which one is set and why. A receiver can then read a value the same way on
every event, e.g. `issue.owners[0].title` on any issue event, instead of a
different path per event type.
Exactly one of `issue`, `incident`, or `ping` is set, matching `event_type`.
Select this format per webhook integration; the default is the nested `Event`.
Payload signing is identical — see `Event` for the signature scheme.
Experimental: this shape may change while we gather feedback.
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | none |
| event\_id | [ string](#string) | none |
| event\_time | [ Timestamp](#timestamp) | none |
| event\_type | [ EventType](#eventtype) | Which event occurred, and which payload field below is set. |
| issue | [ IssueSummary](#issuesummary) | Issue payload. Set on every `EVENT_TYPE_ISSUE_*` event. |
| incident | [ IncidentSummary](#incidentsummary) | Incident payload. Set on every `EVENT_TYPE_INCIDENT_*` event. |
| ping | [ Ping](#ping) | Test payload. Set on `EVENT_TYPE_PING` (sent during webhook setup). |
| callbacks | [repeated Callback](#callback) | none |
### BatchGetDeliveriesRequest
(synq.deliveries.v1.BatchGetDeliveriesRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Delivery ids to fetch (1–200, unique), as returned by `ListDeliveries`, `Redeliver` or `SendTestDelivery`. |
### BatchGetDeliveriesResponse
(synq.deliveries.v1.BatchGetDeliveriesResponse)
| Field | Type | Description |
| - | - | - |
| deliveries | [map DeliveriesEntry](#deliveriesentry) | The deliveries found, keyed by id. Ids that do not exist are absent. |
### BatchGetDeliveriesResponse.DeliveriesEntry
(synq.deliveries.v1.BatchGetDeliveriesResponse.DeliveriesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Delivery](#delivery) | none |
### GetDeliveryRequest
(synq.deliveries.v1.GetDeliveryRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Delivery id. |
### GetDeliveryResponse
(synq.deliveries.v1.GetDeliveryResponse)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_delivery.delivery | [optional Delivery](#delivery) | The delivery. Absent when there is none with that id. |
### ListAttemptsRequest
(synq.deliveries.v1.ListAttemptsRequest)
| Field | Type | Description |
| - | - | - |
| delivery\_id | [ string](#string) | Delivery whose attempts to list. |
### ListAttemptsResponse
(synq.deliveries.v1.ListAttemptsResponse)
| Field | Type | Description |
| - | - | - |
| attempts | [repeated Attempt](#attempt) | Every attempt made, oldest first. Empty for a delivery that sent nothing, and for one still waiting on its first attempt. |
### ListDeliveriesRequest
(synq.deliveries.v1.ListDeliveriesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_integration\_id.integration\_id | [optional string](#string) | Restrict to one integration. Omit for the whole workspace's feed. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_event\_id.event\_id | [optional string](#string) | Restrict to one event — every integration it reached, and what each did with it. This is the `event_id` from a received webhook payload. Setting it searches everything still retained, ignoring `since` and `until`. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_subject\_id.subject\_id | [optional string](#string) | Restrict to the issue or incident the delivery was about. Takes the same identifier the rest of the API uses for that object. Setting it searches everything still retained, ignoring `since` and `until`. |
| event\_types | [repeated string](#string) | Restrict to these event types, by `synq.webhooks.v1.Event.EventType` name. Empty matches every type. |
| outcomes | [repeated Outcome](#outcome) | Restrict to these outcomes. Empty matches every outcome. |
| status\_codes | [repeated int32](#int32) | Restrict to deliveries whose most recent attempt returned one of these HTTP statuses. Empty matches every status. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_since.since | [optional Timestamp](#timestamp) | Only deliveries for events at or after this time. Defaults to the start of the retention window, and is ignored when `event_id` or `subject_id` names what to return. Note that this bounds the event time, which for an issue is when the issue began rather than when the delivery was sent. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_until.until | [optional Timestamp](#timestamp) | Only deliveries for events strictly before this time. Defaults to now, and is ignored when `event_id` or `subject_id` names what to return. |
| pagination | [ Pagination](#pagination) | Pagination. Deliveries are returned newest event first; pass the `last_id` of the previous page as the cursor. |
| include\_routing | [ bool](#bool) | Also return `matched_alert_configs` and `correlation_key` on each delivery. Off by default: it costs a second read per delivery, and a feed rarely needs it. `BatchGetDeliveries` always returns them. |
### ListDeliveriesResponse
(synq.deliveries.v1.ListDeliveriesResponse)
| Field | Type | Description |
| - | - | - |
| deliveries | [repeated Delivery](#delivery) | The matching deliveries, newest event first. |
| page\_info | [ PageInfo](#pageinfo) | Cursor for the next page. |
### OutcomeCount
(synq.deliveries.v1.OutcomeCount)
| Field | Type | Description |
| - | - | - |
| outcome | [ Outcome](#outcome) | none |
| count | [ int32](#int32) | none |
### RedeliverRequest
(synq.deliveries.v1.RedeliverRequest)
| Field | Type | Description |
| - | - | - |
| delivery\_id | [ string](#string) | Delivery to send again. |
### RedeliverResponse
(synq.deliveries.v1.RedeliverResponse)
| Field | Type | Description |
| - | - | - |
| delivery\_id | [ string](#string) | Id of the delivery created by the replay. Poll `BatchGetDeliveries` with it to follow the outcome. |
### SendTestDeliveryRequest
(synq.deliveries.v1.SendTestDeliveryRequest)
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Integration to send the test to. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_message.message | [optional string](#string) | Message to carry in the test payload, so a test can be recognised on the receiving end. Defaults to a generic one. |
### SendTestDeliveryResponse
(synq.deliveries.v1.SendTestDeliveryResponse)
| Field | Type | Description |
| - | - | - |
| delivery\_id | [ string](#string) | Id of the delivery created. Poll `BatchGetDeliveries` with it to follow the outcome. |
### SkipReasonCount
(synq.deliveries.v1.SkipReasonCount)
| Field | Type | Description |
| - | - | - |
| skip\_reason | [ SkipReason](#skipreason) | none |
| count | [ int32](#int32) | none |
### StatusClassCount
(synq.deliveries.v1.StatusClassCount)
| Field | Type | Description |
| - | - | - |
| status\_class | [ StatusClass](#statusclass) | none |
| count | [ int32](#int32) | none |
### SummariseDeliveriesRequest
(synq.deliveries.v1.SummariseDeliveriesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_integration\_id.integration\_id | [optional string](#string) | Restrict to one integration. Omit to summarise the whole workspace. |
| since | [ Timestamp](#timestamp) | Start of the window, on event time. Required — a summary of everything kept is rarely the question, and always the most expensive one. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_until.until | [optional Timestamp](#timestamp) | End of the window, on event time. Defaults to now. |
### SummariseDeliveriesResponse
(synq.deliveries.v1.SummariseDeliveriesResponse)
| Field | Type | Description |
| - | - | - |
| since | [ Timestamp](#timestamp) | The window actually summarised, after defaults were applied. |
| until | [ Timestamp](#timestamp) | none |
| total | [ int32](#int32) | Deliveries in the window, whatever became of them. |
| by\_outcome | [repeated OutcomeCount](#outcomecount) | Counts per outcome. Outcomes with no deliveries are omitted. |
| by\_status\_class | [repeated StatusClassCount](#statusclasscount) | Counts per HTTP status class of the most recent attempt. Deliveries that were never attempted are omitted rather than counted as a class. |
| by\_skip\_reason | [repeated SkipReasonCount](#skipreasoncount) | Counts per reason for sending nothing. Only over the deliveries counted as `OUTCOME_SKIPPED` or `OUTCOME_NOT_ROUTED`. |
### Attempt
(synq.deliveries.v1.Attempt)
Attempt is one try at handing a delivery to its destination, recorded as it
happened: what was sent, what came back, and how long it took.
A delivery can produce more than one outgoing request — an integration that
creates an object and then annotates it makes two — and each request is
retried on its own. Attempts of the same request share `request_id` and count
up in `attempt`.
| Field | Type | Description |
| - | - | - |
| delivery\_id | [ string](#string) | The delivery this attempt belongs to. |
| attempt | [ int32](#int32) | Which try this was for `request_id`, starting at 1. |
| request\_id | [ string](#string) | Opaque identifier of the outgoing request. Group attempts by it to follow one request's retries; compare it to tell two requests of the same delivery apart. |
| attempted\_at | [ Timestamp](#timestamp) | When the attempt started. |
| duration | [ Duration](#duration) | How long it took, whether it succeeded or failed. |
| error | [ string](#string) | Why the attempt never produced a response — a connection failure, a TLS error, a timeout. Empty when a response was received, however it read. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) transport.http | [ HttpAttempt](#httpattempt) | none |
### Header
(synq.deliveries.v1.Header)
Header is one HTTP header of an attempt.
A header whose value carries a credential is never returned, on either the
request or the response side. That covers headers you configured as secret,
credentials held for the destination, and the standard credential-bearing
names (`Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`,
`X-Api-Key`, `X-Auth-Token`) whatever their contents — a destination echoing
its own authorization header back is withheld too.
Signature headers are returned in full: `X-Coalesce-Signature` is a digest of
the body and `X-Coalesce-Timestamp` is a timestamp, neither of which is the
signing secret, and the exact bytes are what you need to reproduce a
verification that failed.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Header name, as sent. |
| value | [ string](#string) | Header value. Empty when `withheld` is set. |
| withheld | [ bool](#bool) | Whether the value was withheld. The header itself is still reported, so the record shows which headers were present. |
| fingerprint | [ string](#string) | Short digest of a withheld value, stable for the same value. Enough to answer "is this the same token as the one that worked yesterday" by comparing two deliveries, without disclosing either value. |
| secret\_version | [ string](#string) | Which version of the stored secret the withheld value was read from, so a delivery can be attributed to a specific secret across a rotation. Empty for values that come from no stored secret. |
### HttpAttempt
(synq.deliveries.v1.HttpAttempt)
HttpAttempt is one HTTP request and the response it got.
Credential-bearing headers are never returned, whichever side they appear on —
see `Header`.
| Field | Type | Description |
| - | - | - |
| method | [ string](#string) | Method used, upper-case. |
| url | [ string](#string) | Full URL the request was sent to, query string included. |
| request\_headers | [repeated Header](#header) | Headers as sent. |
| request\_body | [ string](#string) | Body as sent. A value the destination requires to be secret — a routing key, for instance — is replaced by a marker naming the field it stood in, so the record shows that a value was sent rather than implying none was. |
| request\_bytes | [ int32](#int32) | Size of the body actually sent, in bytes, before any such substitution. |
| status\_code | [ int32](#int32) | HTTP status returned. Zero when no response arrived; `error` on the attempt then says why. |
| response\_headers | [repeated Header](#header) | Headers the destination returned. |
| response\_body | [ string](#string) | Body the destination returned, cut short past a fixed size — see `response_truncated`. |
| response\_bytes | [ int32](#int32) | Size of the response body as the destination sent it, before truncation. |
| response\_truncated | [ bool](#bool) | Whether `response_body` was cut short. When set, `response_bytes` is the full size and `response_body` holds the leading bytes. |
### Delivery
(synq.deliveries.v1.Delivery)
Delivery is one event as it was addressed to one integration: what it was
about, whether it went, and what happened when it did.
One event reaches every integration subscribed to it, so an event that fans
out to three integrations produces three deliveries. They share `event_id`
and each has its own `id`.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque identifier of this delivery, composed by the service. Pass it back to `BatchGetDeliveries`, `ListAttempts` and `Redeliver`; do not parse it or build one yourself, as its composition can change. |
| event\_id | [ string](#string) | Identifier of the event this delivery carries, shared by every integration the same event reached. Filter `ListDeliveries` on it to answer "which of my integrations did this event reach, and what did each of them do with it". It is also the `event_id` in the webhook payload a receiver got, so a customer holding a received webhook can find its delivery from it. |
| integration\_id | [ string](#string) | The integration this delivery was addressed to. |
| event\_type | [ string](#string) | Name of the `synq.webhooks.v1.Event.EventType` the delivery carries — for example `EVENT_TYPE_ISSUE_CREATED`. Empty when the event has no representation for this kind of integration. |
| subject | [ DeliverySubject](#deliverysubject) | What the event is about, so a delivery can be found from the issue or incident a question is asked about. |
| created\_at | [ Timestamp](#timestamp) | When the event happened. This is the event time, not the time it was sent — `last_attempt_at` is that. Deliveries are listed newest event first. |
| outcome | [ Outcome](#outcome) | What became of the delivery. |
| skip\_reason | [ SkipReason](#skipreason) | Why nothing was sent. Set exactly when `outcome` is `OUTCOME_SKIPPED` or `OUTCOME_NOT_ROUTED`. |
| attempt\_count | [ int32](#int32) | How many attempts have been made so far, across every request this delivery produced. Zero for a delivery that sent nothing, and for one still queued. |
| last\_attempt\_at | [ Timestamp](#timestamp) | When the most recent attempt started. Unset until the first attempt. |
| next\_attempt\_at | [ Timestamp](#timestamp) | When the next attempt is due. Set only while `outcome` is `OUTCOME_PENDING`; an attempt can still start slightly later than this under load. |
| duration | [ Duration](#duration) | How long the attempts took in total. Useful as "how long did this take to land" rather than as a measure of one request. |
| status\_code | [ int32](#int32) | HTTP status of the most recent attempt. Zero when no response was received — either nothing has been sent yet, or the request failed before one arrived, in which case the attempt carries the transport error. |
| redelivery\_of | [ string](#string) | The delivery this one replays, set when it was produced by `Redeliver`. Empty for a first delivery. |
| matched\_alert\_configs | [repeated MatchedAlertConfig](#matchedalertconfig) | Which alert configurations selected this integration for this event — the answer to "why did I get this". More than one when several configurations match the same event, because their alerts are merged into a single delivery per integration. Empty for deliveries not produced by alerting, such as a test delivery. |
Always populated by `BatchGetDeliveries`; populated by `ListDeliveries` only when the request sets `include_routing`. |
\| correlation\_key | [ string](#string) | The key the destination deduplicates on — a PagerDuty `dedup_key`, an Opsgenie `alias` — so an incident in your own tool can be traced back to the delivery that opened it. Empty for destinations that create no such object.
Populated on the same terms as `matched_alert_configs`. |
### DeliverySubject
(synq.deliveries.v1.DeliverySubject)
DeliverySubject is the object an event is about.
| Field | Type | Description |
| - | - | - |
| type | [ SubjectType](#subjecttype) | Which kind of object `id` refers to. |
| id | [ string](#string) | Identifier of the issue or incident, as the rest of the API uses it. For a test delivery this is the event id, since a test is about nothing else. |
| title | [ string](#string) | Title at the time the delivery was decided, kept so a delivery stays readable after the issue is renamed or closed. |
| url | [ string](#string) | Link to the object in the app. Empty when the subject has no page. |
### MatchedAlertConfig
(synq.deliveries.v1.MatchedAlertConfig)
MatchedAlertConfig names one alert configuration that selected an integration
for an event.
| Field | Type | Description |
| - | - | - |
| alert\_config\_id | [ string](#string) | Identifier of the alert configuration. |
| name | [ string](#string) | Its name when the delivery was decided, so the delivery stays attributable after the configuration is renamed or deleted. |
| fqn | [ string](#string) | Fully-qualified name, set only for configurations managed as code. |
| ownership\_id | [ string](#string) | The ownership the configuration belongs to. Empty for workspace-level configurations. |
| owner\_path | [ string](#string) | The owner the integration was reached through. Empty when the configuration names the integration directly rather than routing to an owner. |
### IssueTriageState
(synq.agent.sre.v1.IssueTriageState)
IssueTriageState represents the current state of an issue in the SRE agent's investigation workflow.
This state is used to track the progress of automated triage and coordinate between different parts of the system.
| Field | Type | Description |
| - | - | - |
| issue\_id | [ string](#string) | Unique identifier of the issue being investigated by the SRE agent |
| status | [ IssueTriageStatus](#issuetriagestatus) | Current status of the issue in the triage workflow, indicating the stage of investigation |
| last\_updated\_at | [ Timestamp](#timestamp) | Timestamp indicating when the issue triage state was last updated |
| failure\_category | [ TriageFailureCategory](#triagefailurecategory) | Structured failure category for the frontend to render appropriate messages, only set when status is FAILED |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_failure\_details.failure\_details | [optional string](#string) | Raw error details for debugging, only set when status is FAILED. Visible to Coalesce Quality admins. |
### AddToIncidentConclusion
(synq.agent.sre.v1.AddToIncidentConclusion)
AddToIncidentConclusion indicates that the issue should be added to an existing incident.
This conclusion should be supported by evidence showing the connection to the existing incident.
| Field | Type | Description |
| - | - | - |
| incident\_id | [ string](#string) | ID of the existing incident that this issue should be added to. |
### DeclareIncidentConclusion
(synq.agent.sre.v1.DeclareIncidentConclusion)
DeclareIncidentConclusion indicates that the issue requires immediate attention as an incident.
This conclusion should be supported by evidence showing significant impact or risk.
| Field | Type | Description |
| - | - | - |
| title | [ string](#string) | Suggested title for the incident that will be created. Should be clear and concise to quickly communicate the situation. |
| description | [ string](#string) | Detailed description of the incident situation, including: - What is happening - Potential impact - Affected systems or services - Any immediate actions needed |
### IssueConclusion
(synq.agent.sre.v1.IssueConclusion)
IssueConclusion represents the final determination made by the LLM agent about an issue.
Each conclusion type must be supported by appropriate evidence collected during investigation.
| Field | Type | Description |
| - | - | - |
| conclusion\_id | [ string](#string) | Unique identifier of the conclusion. |
| issue\_ids | [repeated string](#string) | List of issue IDs that should be concluded with the specified conclusion. |
| title | [ string](#string) | Brief, single-line description summarizing the triage conclusion. Max 5 words. |
| summary | [ string](#string) | Detailed markdown-formatted description explaining the conclusion and the reasoning based on all collected evidence. |
| concluded\_at | [ Timestamp](#timestamp) | Timestamp when the conclusion was made. |
| is\_final\_conclusion | [ bool](#bool) | Indicates that this is final conclusion and SRE agent won't change it unless new evidence is provided to change it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_retriage\_instruction\_response.retriage\_instruction\_response | [optional RetriageInstructionResponse](#retriageinstructionresponse) | How this conclusion used the retriage instructions a person gave for the issue. Set only when the triage that produced it was given instructions. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.set\_expected | [ SetExpectedConclusion](#setexpectedconclusion) | Conclusion that the issue is expected behavior. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.set\_no\_action\_needed | [ SetNoActionNeededConclusion](#setnoactionneededconclusion) | Conclusion that no action is needed as the issue will resolve itself. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.set\_fixed | [ SetFixedConclusion](#setfixedconclusion) | Conclusion that the issue has been fixed and can be closed. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.declare\_incident | [ DeclareIncidentConclusion](#declareincidentconclusion) | Conclusion that the issue requires incident declaration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.add\_to\_incident | [ AddToIncidentConclusion](#addtoincidentconclusion) | Conclusion that the issue should be added to an existing incident. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.request\_for\_human\_review | [ RequestForHumanReviewConclusion](#requestforhumanreviewconclusion) | Conclusion that human review is needed for further investigation. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) conclusion.unexplained | [ UnexplainedConclusion](#unexplainedconclusion) | Conclusion that the issue cannot be explained and needs urgent human attention. |
### IssueConclusionWithEvidence
(synq.agent.sre.v1.IssueConclusionWithEvidence)
| Field | Type | Description |
| - | - | - |
| conclusion | [ IssueConclusion](#issueconclusion) | IssueConclusion represents the final determination made by the LLM agent about an issue. |
| original\_issue\_ids | [repeated string](#string) | List of issues for which original conclusion was created. |
| evidence | [repeated EvidenceItem](#evidenceitem) | Collection of structured evidence items collected during the investigation. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user\_feedback.user\_feedback | [optional ConclusionFeedback](#conclusionfeedback) | Optional feedback given by the user on the SRE generated conclusion. |
### RequestForHumanReviewConclusion
(synq.agent.sre.v1.RequestForHumanReviewConclusion)
RequestForHumanReviewConclusion indicates that the issue needs human review.
This conclusion should be used when:
* The agent has collected some evidence but cannot make a definitive conclusion
* The issue requires human judgment or expertise
* The impact is unclear or needs human assessment
### RetriageInstructionResponse
(synq.agent.sre.v1.RetriageInstructionResponse)
RetriageInstructionResponse ties a conclusion to the retriage instructions that produced it,
so the instructions and what the agent did with them are read side by side.
| Field | Type | Description |
| - | - | - |
| instructions | [repeated string](#string) | The instructions the triage was given, verbatim, in the order they were submitted. |
| response | [ string](#string) | Markdown statement of how the instructions changed the analysis, or why they did not, including the answer when an instruction asked a question. Empty when the agent concluded without addressing them. |
### SetExpectedConclusion
(synq.agent.sre.v1.SetExpectedConclusion)
SetExpectedConclusion indicates that the issue is expected behavior and no action is needed.
This conclusion should be supported by evidence showing this is normal operation.
### SetFixedConclusion
(synq.agent.sre.v1.SetFixedConclusion)
SetFixedConclusion indicates that the issue has already been resolved and can be closed.
This conclusion should be supported by evidence showing the fix has been applied and verified.
### SetNoActionNeededConclusion
(synq.agent.sre.v1.SetNoActionNeededConclusion)
SetNoActionNeededConclusion indicates that the issue will resolve itself without intervention.
This conclusion should be supported by evidence showing the issue is temporary or self-healing.
### UnexplainedConclusion
(synq.agent.sre.v1.UnexplainedConclusion)
UnexplainedConclusion indicates that the agent was unable to collect sufficient evidence
to understand or explain the issue. This conclusion requires high-priority human review
as it may indicate a serious or complex problem that needs immediate attention.
### TestSuggestionState
(synq.agent.sre.v1.TestSuggestionState)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | none |
| path | [ string](#string) | none |
| identifier | [ Identifier](#identifier) | none |
| status | [ TestSuggestionStatus](#testsuggestionstatus) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| scheduled\_at | [ Timestamp](#timestamp) | none |
| updated\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_user\_prompt.last\_user\_prompt | [optional string](#string) | none |
### AcceptFeedback
(synq.agent.sre.v1.AcceptFeedback)
### ConclusionFeedback
(synq.agent.sre.v1.ConclusionFeedback)
| Field | Type | Description |
| - | - | - |
| user | [ string](#string) | none |
| feedback\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feedback.accept | [ AcceptFeedback](#acceptfeedback) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feedback.dismiss | [ DismissFeedback](#dismissfeedback) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feedback.retriage | [ ReTriageFeedback](#retriagefeedback) | none |
### DismissFeedback
(synq.agent.sre.v1.DismissFeedback)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_comment.comment | [optional string](#string) | none |
### ReTriageFeedback
(synq.agent.sre.v1.ReTriageFeedback)
| Field | Type | Description |
| - | - | - |
| instruction | [ string](#string) | none |
### EvaluateRequest
(synq.agent.sre.v1.EvaluateRequest)
Request to evaluate an LLM request with structured output.
| Field | Type | Description |
| - | - | - |
| output\_schema | [ Struct](#struct) | JSON schema defining the structure of the expected output. The LLM will produce output conforming to this schema. |
| system\_prompt | [ string](#string) | Main system prompt providing instructions to the LLM. This should be constant as it will be cached for efficiency. |
| messages | [repeated Message](#message) | History of messages in the conversation. Must contain at least one message. The last message is used as the final request to the LLM. |
| model\_type | [ ModelType](#modeltype) | Type of model to use for the evaluation. Defaults to MODEL\_TYPE\_SUMMARY if not specified or set to MODEL\_TYPE\_UNSPECIFIED. |
### EvaluateResponse
(synq.agent.sre.v1.EvaluateResponse)
Response from the LLM evaluation.
| Field | Type | Description |
| - | - | - |
| output | [ Struct](#struct) | Structured output from the LLM conforming to the provided schema. |
| metrics | [ LlmResponseMetrics](#llmresponsemetrics) | Metrics about the LLM response. |
### LlmResponseMetrics
(synq.agent.sre.v1.LlmResponseMetrics)
Metrics about the LLM response.
| Field | Type | Description |
| - | - | - |
| input\_tokens | [ int32](#int32) | Number of tokens in the input prompt and messages. |
| output\_tokens | [ int32](#int32) | Number of tokens in the generated output. |
| total\_tokens | [ int32](#int32) | Total number of tokens used (input + output). |
| latency\_ms | [ int64](#int64) | Time taken to generate the response in milliseconds. |
| model | [ string](#string) | Model identifier used for the evaluation. |
| cache\_write\_tokens | [ int32](#int32) | Number of tokens written to the prompt cache. |
| cache\_read\_tokens | [ int32](#int32) | Number of tokens read from the prompt cache. |
### Message
(synq.agent.sre.v1.Message)
A single message in the conversation history.
| Field | Type | Description |
| - | - | - |
| role | [ MessageRole](#messagerole) | Role of the message author. |
| content | [ string](#string) | Content of the message. |
### AcceptPromptSuggestionRequest
(synq.agent.sre.v1.AcceptPromptSuggestionRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_edited\_value.edited\_value | [optional string](#string) | If set, the user edited the suggestion before accepting. The server records both `suggested_value` (unchanged) and `applied_value` (= `edited_value`). Hard-capped at 16384 bytes to match the SRE agent prompt field limits. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_note.note | [optional string](#string) | Optional short free-text note recorded with the decision. Capped at 512 bytes — it is a one-line annotation for the audit trail, not a place to restate the prompt. |
### AcceptPromptSuggestionResponse
(synq.agent.sre.v1.AcceptPromptSuggestionResponse)
| Field | Type | Description |
| - | - | - |
| suggestion | [ PromptSuggestion](#promptsuggestion) | none |
### AgentTask
(synq.agent.sre.v1.AgentTask)
| Field | Type | Description |
| - | - | - |
| task\_id | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deadline\_at.deadline\_at | [optional Timestamp](#timestamp) | Deadline after which the task should not be processed. If set, the agent should skip or abort tasks past this time. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) task.triage | [ TriageTask](#triagetask) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) task.suggest\_tests | [ SuggestTestsTask](#suggestteststask) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) task.evaluate | [ EvaluateTask](#evaluatetask) | none |
### AgentTaskResponse
(synq.agent.sre.v1.AgentTaskResponse)
| Field | Type | Description |
| - | - | - |
| task\_id | [ string](#string) | none |
| message | [ string](#string) | Human visible message |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_error.error | [optional string](#string) | Error message indicating a problem |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) result.evaluate\_response | [ EvaluateResponse](#evaluateresponse) | none |
### ConnectRequest
(synq.agent.sre.v1.ConnectRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) message.hello | [ Hello](#hello) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) message.task\_response | [ AgentTaskResponse](#agenttaskresponse) | none |
### ConnectResponse
(synq.agent.sre.v1.ConnectResponse)
| Field | Type | Description |
| - | - | - |
| tasks | [repeated AgentTask](#agenttask) | none |
### DismissPromptSuggestionRequest
(synq.agent.sre.v1.DismissPromptSuggestionRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reason.reason | [optional string](#string) | Optional short free-text reason recorded with the decision. Capped at 512 bytes — a one-line justification for the audit trail. |
### DismissPromptSuggestionResponse
(synq.agent.sre.v1.DismissPromptSuggestionResponse)
| Field | Type | Description |
| - | - | - |
| suggestion | [ PromptSuggestion](#promptsuggestion) | none |
### DwhConnection
(synq.agent.sre.v1.DwhConnection)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | This will be connection in the agent or integration\_id when managed |
| instance | [ string](#string) | What is the instance of this, e.g. BigQuery project or Databricks workspace |
| type | [ string](#string) | Type of the DWH connection |
| databases | [repeated string](#string) | Databases in this instance, this would be BigQuery dataset or Databricks catalog |
| parameters | [map ParametersEntry](#parametersentry) | Extra parameters present on the connection |
| agent\_access\_enabled | [ bool](#bool) | Indicates that Coalesce Quality managed agent can access this connection |
### DwhConnection.ParametersEntry
(synq.agent.sre.v1.DwhConnection.ParametersEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### EvaluateTask
(synq.agent.sre.v1.EvaluateTask)
Task to evaluate an LLM request on the agent side (for self-hosted agents).
| Field | Type | Description |
| - | - | - |
| request | [ EvaluateRequest](#evaluaterequest) | The evaluate request to be processed by the agent's local LLM. |
### GetConfigRequest
(synq.agent.sre.v1.GetConfigRequest)
### GetConfigResponse
(synq.agent.sre.v1.GetConfigResponse)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Workspace to which agent is authenticated |
| triage\_system\_prompt | [ string](#string) | Extra instructions used while triaging and concluding issues. |
| tester\_system\_prompt | [ string](#string) | Extra instructions used while suggesting tests. |
| workspace\_system\_prompt | [ string](#string) | Extra instructions describing workspace to which agent is connected. |
| synq\_dwh\_connections | [repeated DwhConnection](#dwhconnection) | Description of all DWH connections/integrations configured in Coalesce Quality. Will indicate to the agent that it can't access all the data sources if agent has no connections enabled. |
| issue\_grouping\_system\_prompt | [ string](#string) | Extra instructions describing how issues should be grouped |
| issue\_grouping\_disabled | [ bool](#bool) | Indicates that SRE shouldn't group any issues |
### Hello
(synq.agent.sre.v1.Hello)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| build\_version | [ string](#string) | none |
### ListPromptSuggestionsRequest
(synq.agent.sre.v1.ListPromptSuggestionsRequest)
| Field | Type | Description |
| - | - | - |
| fields | [repeated PromptField](#promptfield) | Empty = include all fields. |
| statuses | [repeated PromptSuggestionStatus](#promptsuggestionstatus) | Empty = pending only. |
### ListPromptSuggestionsResponse
(synq.agent.sre.v1.ListPromptSuggestionsResponse)
| Field | Type | Description |
| - | - | - |
| suggestions | [repeated PromptSuggestion](#promptsuggestion) | none |
### PromptSuggestion
(synq.agent.sre.v1.PromptSuggestion)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Server-assigned UUID. |
| workspace | [ string](#string) | Workspace the suggestion belongs to. Always populated, always matches the caller's workspace for customer-facing reads. |
| field | [ PromptField](#promptfield) | none |
| status | [ PromptSuggestionStatus](#promptsuggestionstatus) | none |
| baseline\_value | [ string](#string) | Value of the targeted prompt field as it was at the moment the suggestion was produced. Used to detect drift on Accept. |
| suggested\_value | [ string](#string) | Value proposed by the analyser. A full replacement for the targeted prompt; diffed against `baseline_value` in the review UI (see the producer guidance on `RecordPromptSuggestionRequest.suggested_value`). |
| rationale | [ string](#string) | Short markdown explanation produced by the analyser (≤ 2048 bytes), rendered as a "why" callout above the diff in the review UI. |
| evidence\_json | [ bytes](#bytes) | Opaque JSON encoded by the producer (e.g. references to source entities and incidents). Returned only to producer-scoped callers; customer reads see an empty value. |
| source | [ string](#string) | Producer identifier + version, e.g. `"offline_audit_v1"`. |
| produced\_at | [ Timestamp](#timestamp) | none |
| reviewed\_by | [ string](#string) | Identity of whoever reviewed the suggestion (derived from the JWT actor). Empty while the suggestion is pending. |
| reviewed\_at | [ Timestamp](#timestamp) | none |
| decision\_note | [ string](#string) | Optional short reason on dismiss, or note on accept (≤ 512 bytes). A one-line annotation captured for the audit trail. |
| applied\_value | [ string](#string) | Value actually written to the prompt field. Differs from `suggested_value` when the user edited before accepting. |
### RecordPromptSuggestionRequest
(synq.agent.sre.v1.RecordPromptSuggestionRequest)
| Field | Type | Description |
| - | - | - |
| field | [ PromptField](#promptfield) | Targets the workspace from the JWT (back-office producers select the workspace via the standard x-synq-workspace-override header). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suggested\_value.suggested\_value | [optional string](#string) | Full replacement value for the targeted prompt field, hard-capped at 16384 bytes to match the SRE agent prompt field limit. |
Explicit-presence (`optional`) so the field can carry an empty string with meaning. An empty value is a deliberate proposal to REMOVE the prompt entirely (clear the override back to the built-in default), distinct from omitting the field. Use this when a customised prompt is judged counter-productive and the workspace is better off with no override at all.
`required` is intentionally NOT set: that rule rejects the empty string, which would make a removal proposal un-expressible. Presence (the field must be set at all) is enforced by the handler via a nil check, so an omitted field is still rejected while an explicit "" is accepted.
Producer guidance — propose the *smallest* change that achieves the goal. Start from the current prompt value and append, delete, or rewrite only the relevant section; do not regenerate the whole prompt from scratch. The reviewer is shown a Current-vs-Suggested diff, so a surgical edit is far easier to approve than a wholesale rewrite that rewords every line.
Keep individual lines short and break content into paragraphs separated by newlines instead of collapsing everything onto one long line. The review UI diffs line by line, so well-broken text produces a readable, minimal diff; a single monster line shows up as one giant replaced block. |
\| rationale | [ string](#string) | Short markdown explanation of why the change is proposed, capped at 2048 bytes. Shown to the reviewer as a "why" callout above the diff, so keep it to a few tight sentences or bullet points — it justifies the change, it does not restate the prompt. As with `suggested_value`, prefer short lines / paragraphs over one long line. |
\| evidence\_json | [ bytes](#bytes) | Opaque JSON: structured pointers the analyser used to produce the suggestion (top entities, sample issues, etc.). Stored verbatim for traceability. |
\| source | [ string](#string) | Producer identifier + version, e.g. `"offline_audit_v1"`. A short slug, not free text — capped at 128 bytes. |
### RecordPromptSuggestionResponse
(synq.agent.sre.v1.RecordPromptSuggestionResponse)
| Field | Type | Description |
| - | - | - |
| suggestion | [ PromptSuggestion](#promptsuggestion) | Empty when the live baseline already matches `suggested_value` (no-op). |
### SuggestTestsTask
(synq.agent.sre.v1.SuggestTestsTask)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated Identifier](#identifier) | none |
### TriageTask
(synq.agent.sre.v1.TriageTask)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | none |
### BatchLatestConclusionsRequest
(synq.agent.sre.v1.BatchLatestConclusionsRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | List of issue IDs for which latest conclusion should be returned. |
### BatchLatestConclusionsResponse
(synq.agent.sre.v1.BatchLatestConclusionsResponse)
| Field | Type | Description |
| - | - | - |
| conclusions | [repeated IssueConclusionWithEvidence](#issueconclusionwithevidence) | none |
### BatchTriageStateRequest
(synq.agent.sre.v1.BatchTriageStateRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | List of issue IDs for which triage status should be returned. |
### BatchTriageStateResponse
(synq.agent.sre.v1.BatchTriageStateResponse)
| Field | Type | Description |
| - | - | - |
| states | [map StatesEntry](#statesentry) | none |
### BatchTriageStateResponse.StatesEntry
(synq.agent.sre.v1.BatchTriageStateResponse.StatesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ IssueTriageState](#issuetriagestate) | none |
### ConcludeRequest
(synq.agent.sre.v1.ConcludeRequest)
Request to conclude the triage investigation for one or more issues.
| Field | Type | Description |
| - | - | - |
| session\_id | [ string](#string) | Unique identifier of the triaging session, used to group related operations. |
| conclusion | [ IssueConclusion](#issueconclusion) | IssueConclusion represents the final determination made by the LLM agent about an issue. Each conclusion type must be supported by appropriate evidence collected during investigation. |
| evidence | [repeated EvidenceItem](#evidenceitem) | Collection of evidence items supporting the conclusion. Evidences previously recorded should not be added here. |
### ConcludeResponse
(synq.agent.sre.v1.ConcludeResponse)
Response for the Conclude operation. Empty as the operation is fire-and-forget.
### ListTriageStateRequest
(synq.agent.sre.v1.ListTriageStateRequest)
| Field | Type | Description |
| - | - | - |
| statuses | [repeated IssueTriageStatus](#issuetriagestatus) | none |
### ListTriageStateResponse
(synq.agent.sre.v1.ListTriageStateResponse)
| Field | Type | Description |
| - | - | - |
| states | [repeated IssueTriageState](#issuetriagestate) | none |
### MarkAsInProgressRequest
(synq.agent.sre.v1.MarkAsInProgressRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | List of issue IDs for which work has started. |
### MarkAsInProgressResponse
(synq.agent.sre.v1.MarkAsInProgressResponse)
| Field | Type | Description |
| - | - | - |
| marked\_issue\_ids | [repeated string](#string) | none |
### MarkFailedRequest
(synq.agent.sre.v1.MarkFailedRequest)
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | List of issue IDs for which work has failed. |
| error\_message | [ string](#string) | none |
### MarkFailedResponse
(synq.agent.sre.v1.MarkFailedResponse)
| Field | Type | Description |
| - | - | - |
| marked\_issue\_ids | [repeated string](#string) | none |
### RecordEvidenceRequest
(synq.agent.sre.v1.RecordEvidenceRequest)
Request to record evidence collected during the triage investigation.
| Field | Type | Description |
| - | - | - |
| session\_id | [ string](#string) | Unique identifier of the triaging session, used to group related operations. |
| evidence | [repeated EvidenceItem](#evidenceitem) | Collection of structured evidence items collected during the investigation. Must contain at least one evidence item. |
### RecordEvidenceResponse
(synq.agent.sre.v1.RecordEvidenceResponse)
Response for the RecordEvidence operation. Empty as the operation is fire-and-forget.
### SubmitFeatureRequestRequest
(synq.agent.sre.v1.SubmitFeatureRequestRequest)
Request to submit a feature request. Should only be used as a last resort when no
existing tool can handle the user's request.
| Field | Type | Description |
| - | - | - |
| description | [ string](#string) | A clear description of what the user needs, including context about what they were trying to accomplish and why the current tools are insufficient. This should only be populated when no existing tool can fulfill the request. |
### SubmitFeatureRequestResponse
(synq.agent.sre.v1.SubmitFeatureRequestResponse)
Response for the SubmitFeatureRequest operation.
### BatchSuggestionStatesRequest
(synq.agent.sre.v1.BatchSuggestionStatesRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### BatchSuggestionStatesResponse
(synq.agent.sre.v1.BatchSuggestionStatesResponse)
| Field | Type | Description |
| - | - | - |
| test\_suggestion\_states | [repeated TestSuggestionState](#testsuggestionstate) | none |
### GetActiveSuggestionStatesForRequest
(synq.agent.sre.v1.GetActiveSuggestionStatesForRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### GetActiveSuggestionStatesForResponse
(synq.agent.sre.v1.GetActiveSuggestionStatesForResponse)
| Field | Type | Description |
| - | - | - |
| test\_suggestion\_states | [repeated TestSuggestionState](#testsuggestionstate) | none |
### ListSuggestionStatesRequest
(synq.agent.sre.v1.ListSuggestionStatesRequest)
| Field | Type | Description |
| - | - | - |
| statuses | [repeated TestSuggestionStatus](#testsuggestionstatus) | none |
### ListSuggestionStatesResponse
(synq.agent.sre.v1.ListSuggestionStatesResponse)
| Field | Type | Description |
| - | - | - |
| test\_suggestion\_states | [repeated TestSuggestionState](#testsuggestionstate) | none |
### MarkAsCancelledRequest
(synq.agent.sre.v1.MarkAsCancelledRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### MarkAsCancelledResponse
(synq.agent.sre.v1.MarkAsCancelledResponse)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### MarkAsCompletedRequest
(synq.agent.sre.v1.MarkAsCompletedRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### MarkAsCompletedResponse
(synq.agent.sre.v1.MarkAsCompletedResponse)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### MarkAsFailedRequest
(synq.agent.sre.v1.MarkAsFailedRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
| error\_message | [ string](#string) | none |
### MarkAsFailedResponse
(synq.agent.sre.v1.MarkAsFailedResponse)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### MarkAsRunningRequest
(synq.agent.sre.v1.MarkAsRunningRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### MarkAsRunningResponse
(synq.agent.sre.v1.MarkAsRunningResponse)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### SuggestForRequest
(synq.agent.sre.v1.SuggestForRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
| user\_prompt | [ string](#string) | none |
### SuggestForResponse
(synq.agent.sre.v1.SuggestForResponse)
### Config
(synq.agent.sre.v1.Config)
Config represents the main configuration for the SRE agent
| Field | Type | Description |
| - | - | - |
| synq | [ SYNQ](#synq) | Coalesce Quality platform configuration |
| connections | [map ConnectionsEntry](#connectionsentry) | Map of connection configurations |
| llm | [ LlmConfig](#llmconfig) | none |
### Config.ConnectionsEntry
(synq.agent.sre.v1.Config.ConnectionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Connection](#connection) | none |
### LlmConfig
(synq.agent.sre.v1.LlmConfig)
| Field | Type | Description |
| - | - | - |
| openai | [ OpenAIApi](#openaiapi) | none |
| triage\_parallelism | [ int32](#int32) | How many Issue triages can run in parallel |
| test\_suggestions\_parallelism | [ int32](#int32) | How many test suggestions can be processed in parallel |
### OpenAIApi
(synq.agent.sre.v1.OpenAIApi)
| Field | Type | Description |
| - | - | - |
| base\_url | [ string](#string) | Base URL of the OpenAPI compatible endpoint |
| api\_key | [ string](#string) | API key to use for the interaction with the API |
| thinking\_model | [ string](#string) | Model to use for complex operations |
| summary\_model | [ string](#string) | Model to use for simpler operations, e.g. summary generation or improving the format of messages. |
| thinking\_effort | [ string](#string) | How long the thinking model deliberates before each answer: "none" (answer without thinking first), "low", "medium" or "high". Empty leaves it to the deployment default. It is a cost decision, not a capability one — the agent works at every level. |
Free-form rather than an enum because the levels belong to whichever provider serves the model, and they keep being added. The value reaches the provider as given, so a level this build has never heard of works; one the model refuses is dropped for that request and logged. |
### AnalysisEvidence
(synq.agent.sre.v1.AnalysisEvidence)
AnalysisEvidence contains detailed analysis of the issue based on collected data.
This should only be used as a last resort when no other more specialized evidence types
(history, root cause, related issues, or changes) could be collected.
It provides a comprehensive understanding of the problem and its implications.
| Field | Type | Description |
| - | - | - |
| analysis | [ string](#string) | Detailed analysis of the issue, including observations, patterns, and potential implications. This should provide clear reasoning for the conclusions drawn. |
| sql\_queries | [repeated string](#string) | SQL queries which were executed to investigate the issue and collect the evidence. |
### ChangeEvidence
(synq.agent.sre.v1.ChangeEvidence)
ChangeEvidence documents recent changes that may have contributed to the issue.
This helps in understanding if the issue is related to recent modifications.
| Field | Type | Description |
| - | - | - |
| entities | [repeated Identifier](#identifier) | List of entities (e.g., services, configurations) that have recently changed and may be related to the current issue. |
| entities\_synq\_paths | [repeated string](#string) | Read-only list of paths matching entities. This is a unique path that can be used to access the entity in the Coalesce Quality UI. |
| change\_description | [ string](#string) | Detailed description of the changes that were detected and their potential impact on the current issue. |
| change\_ids | [repeated string](#string) | When available identifiers of specific change IDs |
### CodeChange
(synq.agent.sre.v1.CodeChange)
CodeChange represents a specific change in a file within a commit.
| Field | Type | Description |
| - | - | - |
| file\_path | [ string](#string) | Path to the file that was changed. |
| start\_line | [ int32](#int32) | Starting line number of the change (inclusive). |
| end\_line | [ int32](#int32) | Ending line number of the change (inclusive). |
| change\_description | [ string](#string) | Description of what was changed in this section. |
### CommitEvidence
(synq.agent.sre.v1.CommitEvidence)
CommitEvidence documents code changes that may have contributed to the issue.
This helps in understanding if the issue is related to recent code modifications.
| Field | Type | Description |
| - | - | - |
| change\_id | [ string](#string) | ID of the change that introduced the code modifications. |
| relevant\_code\_changes | [repeated CodeChange](#codechange) | List of relevant code changes in the commit. |
### DataQueryEvidenceItem
(synq.agent.sre.v1.DataQueryEvidenceItem)
DataQueryEvidenceItem represents a single SQL query and its analysis.
| Field | Type | Description |
| - | - | - |
| query\_id | [ string](#string) | DEPRECATED: Previously held the SQL query string despite being named query\_id. Use the `sql` field instead. Kept for backward compatibility — readers should prefer `sql` when present, falling back to `query_id` for older evidence. |
| data\_analysis | [ string](#string) | Analysis specific to this query's results and their implications. |
| sql | [ string](#string) | The SQL query that was executed. |
### DataQueryEvidenceV2
(synq.agent.sre.v1.DataQueryEvidenceV2)
DataQueryEvidenceV2 contains SQL queries and their analysis results with query IDs.
This helps in understanding the issue through data analysis with query tracking.
| Field | Type | Description |
| - | - | - |
| items | [repeated DataQueryEvidenceItem](#dataqueryevidenceitem) | List of data query evidence items, each containing a query ID and its analysis. |
### EvidenceItem
(synq.agent.sre.v1.EvidenceItem)
EvidenceItem represents a single piece of evidence collected during the triage investigation.
Each evidence item must have a title and one of the specific evidence types.
| Field | Type | Description |
| - | - | - |
| evidence\_id | [ string](#string) | Unique identifier of the evidence item. |
| issue\_ids | [repeated string](#string) | List of issue IDs that should be annotated with the provided evidence. Each ID must be a valid UUID. |
| title | [ string](#string) | Brief, descriptive title summarizing the type of evidence collected. This helps in quickly understanding the nature of the evidence. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.issue\_history | [ HistoryEvidence](#historyevidence) | Historical evidence showing similar past issues that may help understand the current situation. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.root\_cause | [ RootCauseEvidence](#rootcauseevidence) | Evidence pointing to the likely root cause of the issue. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.related\_issue | [ RelatedIssueEvidence](#relatedissueevidence) | Evidence showing related issues that may be connected to the current investigation. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.change | [ ChangeEvidence](#changeevidence) | Evidence of recent changes that may have contributed to the issue. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.analysis | [ AnalysisEvidence](#analysisevidence) | Evidence containing detailed analysis of the issue. DEPRECATED: This type will be gradually retired in favor of more specific evidence types like DataQueryEvidenceV2 and OtherEvidence. Please use those types for new evidence. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.commit | [ CommitEvidence](#commitevidence) | Evidence containing code changes and commit information. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.data\_query\_v2 | [ DataQueryEvidenceV2](#dataqueryevidencev2) | Evidence containing SQL queries and data analysis results with query IDs. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.other | [ OtherEvidence](#otherevidence) | Evidence containing general analysis that doesn't fit into other specialized types. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.impacted\_dataproduct | [ ImpactedDataproductEvidence](#impacteddataproductevidence) | Evidence of impact on important dataproduct |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) evidence.impacted\_owner | [ ImpactedOwnerEvidence](#impactedownerevidence) | Evidence of serious impact on business owner |
### HistoryEvidence
(synq.agent.sre.v1.HistoryEvidence)
HistoryEvidence represents historical context from similar past issues.
This helps in understanding if the current issue is recurring or has known patterns.
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | List of issue IDs that are similar to the currently triaged issue. These historical issues can provide insights into resolution patterns. |
### ImpactedDataproductEvidence
(synq.agent.sre.v1.ImpactedDataproductEvidence)
ImpactedDataproduct documents Data Products that are significantly affected by the issue.
This helps in understanding the business impact and prioritizing resolution efforts.
| Field | Type | Description |
| - | - | - |
| impact\_reason | [ string](#string) | Detailed explanation of how and why the dataproduct is impacted by the issue. This should provide clear reasoning for the impact assessment. |
| dataproduct\_ids | [repeated string](#string) | List of dataproduct IDs that are affected by the issue. These IDs should reference existing dataproducts in the system. |
### ImpactedOwnerEvidence
(synq.agent.sre.v1.ImpactedOwnerEvidence)
ImpactedOwner documents business owners or teams that are seriously affected by the issue.
This helps in understanding stakeholder impact and communication requirements.
| Field | Type | Description |
| - | - | - |
| impact\_reason | [ string](#string) | Detailed explanation of how and why the owner is impacted by the issue. This should provide clear reasoning for the impact assessment. |
| owner\_ids | [repeated string](#string) | List of owner IDs that are affected by the issue. These IDs should reference existing owners or teams in the system. |
### OtherEvidence
(synq.agent.sre.v1.OtherEvidence)
OtherEvidence contains general analysis that doesn't fit into other specialized evidence types.
This should be used when the analysis doesn't involve SQL queries, code changes, or other
specialized evidence types.
| Field | Type | Description |
| - | - | - |
| analysis | [ string](#string) | Detailed analysis of the issue, including observations, patterns, and potential implications. This should provide clear reasoning for the conclusions drawn. |
### RelatedIssueEvidence
(synq.agent.sre.v1.RelatedIssueEvidence)
RelatedIssueEvidence identifies other issues that may be connected to the current investigation.
This helps in understanding broader context and potential dependencies.
| Field | Type | Description |
| - | - | - |
| issue\_ids | [repeated string](#string) | List of issue IDs that are related to the current investigation. These issues may be symptoms of the same underlying problem or have dependencies. |
### RootCauseEvidence
(synq.agent.sre.v1.RootCauseEvidence)
RootCauseEvidence identifies the likely root cause of the issue being investigated.
This helps in understanding the fundamental reason behind the problem.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_issue\_id.issue\_id | [optional string](#string) | ID of the issue that is believed to be the root cause of the current issue. |
| entity\_ids | [repeated string](#string) | IDs of entities that are identified as the root cause. |
| execution\_ids | [repeated string](#string) | IDs of executions that are identified as the root cause. |
| analysis | [ string](#string) | Analysis text describing the root cause. |
### Connection
(synq.agent.dwh.v1.Connection)
Connection represents a database connection configuration
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the connection |
| disabled | [ bool](#bool) | none |
| parallelism | [ int32](#int32) | How many queries to DWH can be executed in parallel, defaults to 2 |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.bigquery | [ BigQueryConf](#bigqueryconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.clickhouse | [ ClickhouseConf](#clickhouseconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.databricks | [ DatabricksConf](#databricksconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.mysql | [ MySQLConf](#mysqlconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.postgres | [ PostgresConf](#postgresconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.redshift | [ RedshiftConf](#redshiftconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.snowflake | [ SnowflakeConf](#snowflakeconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.trino | [ TrinoConf](#trinoconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.mssql | [ MSSQLConf](#mssqlconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.oracle | [ OracleConf](#oracleconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.duckdb | [ DuckDBConf](#duckdbconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.athena | [ AthenaConf](#athenaconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.fabric | [ FabricConf](#fabricconf) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.db2 | [ Db2Conf](#db2conf) | none |
### AgentTask
(synq.agent.dwh.v1.AgentTask)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| task\_id | [ string](#string) | none |
| scheduled\_at | [ Timestamp](#timestamp) | When this task should execute, if none is specified it will use time of execution |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deadline\_at.deadline\_at | [optional Timestamp](#timestamp) | After what time this task should stop executing (even in case of errors, retries will be ignored after this time) |
| created\_at | [ Timestamp](#timestamp) | When the task was created |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) command.fetch\_full\_catalog | [ FetchFullCatalogCommand](#fetchfullcatalogcommand) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) command.fetch\_full\_metrics | [ FetchFullMetricsCommand](#fetchfullmetricscommand) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) command.fetch\_segments | [ FetchSegmentsCommand](#fetchsegmentscommand) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) command.fetch\_metrics | [ FetchMetricsCommand](#fetchmetricscommand) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) command.fetch\_table\_stats | [ FetchTableStatsCommand](#fetchtablestatscommand) | none |
### AgentTaskData
(synq.agent.dwh.v1.AgentTaskData)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.fetch\_segments\_result | [ FetchSegmentsResult](#fetchsegmentsresult) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.fetch\_metrics\_result | [ FetchMetricsResult](#fetchmetricsresult) | none |
### FetchFullCatalogCommand
(synq.agent.dwh.v1.FetchFullCatalogCommand)
### FetchFullMetricsCommand
(synq.agent.dwh.v1.FetchFullMetricsCommand)
### FetchMetricsCommand
(synq.agent.dwh.v1.FetchMetricsCommand)
| Field | Type | Description |
| - | - | - |
| sql\_queries | [repeated SqlQuery](#sqlquery) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_command\_context.command\_context | [optional string](#string) | Application specific context around metrics retrieval |
### FetchMetricsResult
(synq.agent.dwh.v1.FetchMetricsResult)
| Field | Type | Description |
| - | - | - |
| query\_rows | [repeated QueryRows](#queryrows) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_command\_context.command\_context | [optional string](#string) | Matches command\_context of FetchMetricsCommand / FetchTableStatsCommand |
### FetchMetricsResult.ColumnValue
(synq.agent.dwh.v1.FetchMetricsResult.ColumnValue)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| is\_null | [ bool](#bool) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) value.double\_value | [ double](#double) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) value.int\_value | [ int64](#int64) | Integer value for values that fit within the signed 64-bit range \[-9223372036854775808, 9223372036854775807]. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) value.time\_value | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) value.big\_int\_value | [ string](#string) | Arbitrary-precision integer encoded as a base-10 string, used for values that exceed the signed 64-bit range. Covers DuckDB HUGEINT (128-bit), ClickHouse Int128/UInt128/Int256/UInt256, and similar wide integer types from other warehouses. Consumers should parse this with an arbitrary-precision library (e.g., Go's math/big.Int, Java's BigInteger, Python's int). |
### FetchMetricsResult.MetricsRow
(synq.agent.dwh.v1.FetchMetricsResult.MetricsRow)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_segment.segment | [optional string](#string) | none |
| column\_values | [repeated ColumnValue](#columnvalue) | none |
### FetchMetricsResult.QueryRows
(synq.agent.dwh.v1.FetchMetricsResult.QueryRows)
| Field | Type | Description |
| - | - | - |
| rows | [repeated MetricsRow](#metricsrow) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_query\_id.query\_id | [optional string](#string) | none |
### FetchSegmentsCommand
(synq.agent.dwh.v1.FetchSegmentsCommand)
| Field | Type | Description |
| - | - | - |
| sql\_query | [ SqlQuery](#sqlquery) | none |
### FetchSegmentsResult
(synq.agent.dwh.v1.FetchSegmentsResult)
| Field | Type | Description |
| - | - | - |
| rows | [repeated SegmentRow](#segmentrow) | none |
### FetchSegmentsResult.SegmentRow
(synq.agent.dwh.v1.FetchSegmentsResult.SegmentRow)
| Field | Type | Description |
| - | - | - |
| segment | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_count.count | [optional int64](#int64) | none |
### FetchTableStatsCommand
(synq.agent.dwh.v1.FetchTableStatsCommand)
| Field | Type | Description |
| - | - | - |
| scope\_filter | [ ScopeFilter](#scopefilter) | Scope filter defining which tables to collect stats for. For a single-table monitor, set a single include rule with exact database/schema/table. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_command\_context.command\_context | [optional string](#string) | Application-specific context, echoed back in the result (mirrors FetchMetricsCommand). |
### SqlQuery
(synq.agent.dwh.v1.SqlQuery)
| Field | Type | Description |
| - | - | - |
| sql | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_limit\_results.limit\_results | [optional int64](#int64) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_query\_id.query\_id | [optional string](#string) | none |
### GenerateConnectionsYamlRequest
(synq.agent.dwh.v1.GenerateConnectionsYamlRequest)
Request to generate YAML configuration from data warehouse connections.
| Field | Type | Description |
| - | - | - |
| connections | [map ConnectionsEntry](#connectionsentry) | Map of connection ID to Connection configuration. Typically obtained from ListConnectionsResponse. Note: Validation is disabled for this field since Connection values may have empty credentials (which will be replaced with env var placeholders). Connection ID validation is handled in the service implementation. |
### GenerateConnectionsYamlRequest.ConnectionsEntry
(synq.agent.dwh.v1.GenerateConnectionsYamlRequest.ConnectionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Connection](#connection) | none |
### GenerateConnectionsYamlResponse
(synq.agent.dwh.v1.GenerateConnectionsYamlResponse)
Response containing the generated YAML configuration.
| Field | Type | Description |
| - | - | - |
| yaml | [ string](#string) | Generated YAML configuration string. Can be written directly to a file and used by DWH Agent or Coalesce Quality Scout. Includes header comments with documentation links. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_error.error | [optional string](#string) | Error message if YAML generation failed. If set, the yaml field should be ignored. |
### ListConnectionsRequest
(synq.agent.dwh.v1.ListConnectionsRequest)
| Field | Type | Description |
| - | - | - |
| connection\_ids | [repeated string](#string) | Optional filter to only return connections with these ids. If empty, all DWH connections are returned. |
### ListConnectionsResponse
(synq.agent.dwh.v1.ListConnectionsResponse)
| Field | Type | Description |
| - | - | - |
| connections | [map ConnectionsEntry](#connectionsentry) | none |
### ListConnectionsResponse.ConnectionsEntry
(synq.agent.dwh.v1.ListConnectionsResponse.ConnectionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Connection](#connection) | none |
### AgentTaskResponse
(synq.agent.dwh.v1.AgentTaskResponse)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| task\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_error.error | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_task\_data.task\_data | [optional AgentTaskData](#agenttaskdata) | none |
### ConnectRequest
(synq.agent.dwh.v1.ConnectRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) message.hello | [ Hello](#hello) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) message.log | [ Log](#log) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) message.task\_response | [ AgentTaskResponse](#agenttaskresponse) | none |
### ConnectResponse
(synq.agent.dwh.v1.ConnectResponse)
| Field | Type | Description |
| - | - | - |
| tasks | [repeated AgentTask](#agenttask) | none |
### Hello
(synq.agent.dwh.v1.Hello)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| build\_version | [ string](#string) | none |
| build\_time | [ string](#string) | none |
| available\_connections | [repeated AvailableConnection](#availableconnection) | none |
### Hello.AvailableConnection
(synq.agent.dwh.v1.Hello.AvailableConnection)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | Identifier which will receive commands |
| name | [ string](#string) | User provided connection name |
| disabled | [ bool](#bool) | Indicates that agent has that connection disabled |
| type | [ string](#string) | Type of the DWH, e.g. bigquery, duckdb, clickhouse |
| instance | [ string](#string) | This will be hostname in most cases |
| databases | [repeated string](#string) | Enabled databases to query if connection supports multiple databases, can be empty. |
| login | [ string](#string) | Login the connection runs its queries as, spelled as the platform's query history names the user: a user name, a service account's email, a service principal's client id. Empty when the configuration names none and only the credential knows it, such as an access token. |
### Log
(synq.agent.dwh.v1.Log)
| Field | Type | Description |
| - | - | - |
| time | [ Timestamp](#timestamp) | none |
| message | [ string](#string) | none |
| level | [ LogLevel](#loglevel) | none |
### AthenaConf
(synq.agent.dwh.v1.AthenaConf)
Amazon Athena specific configuration.
Athena is serverless: there is no host/port — the endpoint is implied by
the AWS region. Queries run inside a workgroup that owns the per-query
data-scan cap and the S3 query result location. The configured workgroup
MUST have ResultConfiguration.OutputLocation set, otherwise every Athena
query fails. Setting EnforceWorkGroupConfiguration=true on the workgroup
is strongly recommended so per-query overrides cannot escape the cap.
Authentication
AthenaConf carries an AWS credential source in one of three ways:
1. Static IAM access key — set access\_key\_id and secret\_access\_key
(and optionally session\_token if those keys are short-lived STS
credentials).
2. Shared-config profile — set aws\_profile to a named profile from
\~/.aws/credentials or \~/.aws/config. Useful when the agent host
already has AWS CLI configured.
3. AWS default credential chain — leave all of the above empty. The
AWS SDK then uses environment variables (AWS\_ACCESS\_KEY\_ID /
AWS\_SECRET\_ACCESS\_KEY / AWS\_PROFILE), shared config, or the EC2 /
ECS / EKS instance role attached to the agent host.
If more than one source is provided, they resolve in the order above —
explicit static keys win over a profile, profile wins over the chain.
role\_arn is independent of the source choice. When set, the executor
uses one of the three sources as the calling identity and assumes
role\_arn via STS for every Athena/Glue API call. external\_id is
included in the AssumeRole request when set; required by trust policies
that mandate it.
Validation: access\_key\_id and secret\_access\_key must be set as a pair
(one without the other is rejected); session\_token may only be set when
both keys are also set.
| Field | Type | Description |
| - | - | - |
| region | [ string](#string) | AWS region hosting the Athena service and Glue Data Catalog. |
| workgroup | [ string](#string) | Athena workgroup. Defaults to "primary" when empty. Must have a ResultConfiguration.OutputLocation configured. |
| catalog | [ string](#string) | Glue Data Catalog name. Defaults to "AwsDataCatalog" when empty. Override only when using federated catalogs backed by Lambda connectors. |
| access\_key\_id | [ string](#string) | AWS access key ID for static IAM credentials. Required together with secret\_access\_key. |
| secret\_access\_key | [ string](#string) | AWS secret access key. Required together with access\_key\_id. |
| session\_token | [ string](#string) | STS session token, set only when access\_key\_id and secret\_access\_key are short-lived STS credentials. Ignored otherwise. |
| aws\_profile | [ string](#string) | Named profile from the AWS shared credentials / config files (\~/.aws/credentials, \~/.aws/config). Useful when the agent host already has AWS CLI configured. |
| role\_arn | [ string](#string) | IAM role to assume via STS for every Athena / Glue API call. The calling identity comes from access\_key\_id, aws\_profile, or the default credential chain — whichever is configured. |
| external\_id | [ string](#string) | ExternalId included in the AssumeRole request. Set when the role's trust policy requires it. |
| role\_session\_name | [ string](#string) | Session name attached to the AssumeRole request. Defaults to "synq-athena-agent". |
| scope | [ ScopeFilter](#scopefilter) | Scope filter for include/exclude filtering by Glue catalog, Glue database, and table. Mapping (mirrors BigQuery's project/dataset shape): ScopeRule.database = Glue Data Catalog name (almost always 'AwsDataCatalog') ScopeRule.schema = Glue database — what users typically filter on ScopeRule.table = Glue table / view name When include rules constrain schemas, the IAM principal does not need catalog-wide glue:GetDatabases — only glue:GetDatabase on the listed names. |
| use\_show\_create\_table | [ bool](#bool) | Use SHOW CREATE TABLE to retrieve full table DDL (CTAS bodies, Iceberg TBLPROPERTIES, Hive external LOCATION/SerDe). One Athena query per table — billed at the 10MB scan minimum each. |
| use\_show\_create\_view | [ bool](#bool) | Use SHOW CREATE VIEW to retrieve full view DDL instead of the rewritten body from information\_schema.views.view\_definition. |
| use\_iceberg\_metrics\_scan | [ bool](#bool) | For Iceberg tables, fan out one Athena query per table to read row counts and total file size from the table's `$files` / `$snapshots` / `$partitions` metadata tables. Without this, Iceberg tables only have metrics when the customer has run ANALYZE TABLE COMPUTE STATISTICS (Glue parameters). Hive external tables are unaffected. Each call is one Athena query (\~\$0.00005 at the 10MB scan minimum). |
### BigQueryConf
(synq.agent.dwh.v1.BigQueryConf)
BigQuery specific configuration
| Field | Type | Description |
| - | - | - |
| project\_id | [ string](#string) | GCP project ID |
| service\_account\_key | [ string](#string) | Service account key JSON |
| service\_account\_key\_file | [ string](#string) | Location of service account key file |
| region | [ string](#string) | Region for BigQuery resources |
| datasets | [repeated string](#string) | Explicit list of dataset names to scrape (e.g., \['analytics', 'mart\_core']). When set, only these datasets are queried — the service account does not need project-level bigquery.datasets.list permission. When empty, all visible datasets in the project are discovered automatically. |
### ClickhouseClusterConf
(synq.agent.dwh.v1.ClickhouseClusterConf)
How metadata reads address ClickHouse system tables.
System tables are per-node, so on a service with more than one replica a plain
read reflects whichever replica answered rather than the whole warehouse.
| Field | Type | Description |
| - | - | - |
| mode | [ ClickhouseClusterMode](#clickhouseclustermode) | none |
| name | [ string](#string) | Cluster to read through, as named under remote\_servers in the ClickHouse configuration. `SELECT DISTINCT cluster FROM system.clusters` lists what a server has. Empty means "default". Ignored in single-node mode. |
### ClickhouseConf
(synq.agent.dwh.v1.ClickhouseConf)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address |
| port | [ int32](#int32) | Port number (default: 9000 for native protocol) |
| database | [ string](#string) | Database the connection opens with, so unqualified table names in a query resolve against it. It does not restrict what is scraped: metadata is read from system tables and covers every database the user can see, whatever this says. Leave it empty to open on "default". |
| username | [ string](#string) | Username for authentication |
| password | [ string](#string) | Password for authentication |
| allow\_insecure | [ bool](#bool) | Whether to use disable SSL for connection |
| settings | [map SettingsEntry](#settingsentry) | ClickHouse server settings applied to every connection, written as they would be in a DSN query string (e.g. "max\_execution\_time": "300"). Values are typed the way ClickHouse types them in a connection string: "true" and "false" become 1 and 0, whole numbers become integers, anything else is passed through as text. A name given here replaces the value the agent would otherwise use. |
| cluster | [ ClickhouseClusterConf](#clickhouseclusterconf) | How ClickHouse system tables are read. Omit to keep reading across a cluster named "default", which every ClickHouse Cloud service provides. |
| instance\_name | [ string](#string) | Name this ClickHouse is published under, and the top element of every path and breadcrumb its tables appear in — `prod`, `staging`, `eu-analytics`. |
ClickHouse has no container above a database, so something has to name the service itself. Left empty, that is the connection host, which is correct but unreadable for a ClickHouse Cloud endpoint; a name given here replaces it.
Two connections to the same service must give the same name, and a name is the identity of everything scraped through it: changing it republishes those tables under new paths, and the old ones are no longer produced. Pick one per service and keep it. |
### ClickhouseConf.SettingsEntry
(synq.agent.dwh.v1.ClickhouseConf.SettingsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### DatabricksConf
(synq.agent.dwh.v1.DatabricksConf)
| Field | Type | Description |
| - | - | - |
| workspace\_url | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_auth\_token.auth\_token | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_auth\_client.auth\_client | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_auth\_secret.auth\_secret | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_warehouse.warehouse | [optional string](#string) | none |
| refresh\_table\_metrics | [ bool](#bool) | none |
| refresh\_table\_metrics\_use\_scan | [ bool](#bool) | none |
| fetch\_table\_tags | [ bool](#bool) | none |
| use\_show\_create\_table | [ bool](#bool) | none |
### Db2Conf
(synq.agent.dwh.v1.Db2Conf)
IBM Db2 for Linux, UNIX and Windows (Db2 LUW) configuration.
Each connection opens one database, the one you would name in
`CONNECT TO `. An instance can hold several databases, each with
its own SYSCAT catalog views, so add a connection for every database you
want to monitor.
The keyword in parentheses after each field is the matching keyword of a
Db2 CLI connection string, db2cli.ini or db2dsdriver.cfg. Values are the
same, so you can copy them from a data source you already use. You don't
need to install a Db2 client, Data Server Driver or clidriver. Db2 for z/OS
and Db2 for i are not supported.
| Field | Type | Description |
| - | - | - |
| hostname | [ string](#string) | Host name or IP address of the Db2 server (HOSTNAME). |
| port | [ int32](#int32) | TCP/IP port of the instance (PORT): the value of the SVCENAME database manager configuration parameter, or SSL\_SVCENAME when security is "SSL" (`db2 get dbm cfg`). Defaults to 50000, or 50001 when security is "SSL". |
| database | [ string](#string) | Database name or alias (DATABASE), as in `CONNECT TO `. |
| user | [ string](#string) | Authorization ID to connect with (UID). Db2 checks it through the instance's security plugin: against the server's operating system by default, or against LDAP when an LDAP plugin is configured. |
| password | [ string](#string) | Password of the authorization ID (PWD). |
| security | [ string](#string) | Set to "SSL" to connect over SSL/TLS (SECURITY=SSL). The instance needs SSL\_SVCENAME set and SSL listed in DB2COMM. Leave empty for a plain TCP/IP connection. |
| ssl\_server\_certificate\_file | [ string](#string) | Path to the server's certificate, or the certificate of the CA that signed it, in PEM format (SSLServerCertificate). The .arm file you get from the instance's keystore with `gsk8capicmd_64 -cert -extract` works as is. The file must exist on the machine that connects to Db2. When it can't, paste the certificate into ssl\_server\_certificate\_pem instead. Needs security set to "SSL". Leave both empty to check the server against the system trust store. |
| ssl\_server\_certificate\_pem | [ string](#string) | Content of the same certificate, starting with "-----BEGIN CERTIFICATE-----", for when you can't point to a file. Needs security set to "SSL". |
| authentication | [ string](#string) | How the authorization ID and password travel to the server (AUTHENTICATION). The values match the database manager configuration parameter of the same name: - "SERVER" (default): sent unencrypted. Use it with security "SSL" on an untrusted network. - "SERVER\_ENCRYPT": sent encrypted. The instance's AUTHENTICATION must accept encrypted credentials, for example SERVER\_ENCRYPT. |
### DuckDBConf
(synq.agent.dwh.v1.DuckDBConf)
DuckDB / MotherDuck specific configuration.
Supports two modes:
1. Local/embedded: set database to a file path (e.g. "/tmp/my.duckdb") or ":memory:"
2. MotherDuck (cloud): set motherduck\_account and motherduck\_token
| Field | Type | Description |
| - | - | - |
| database | [ string](#string) | Local database path (file path or ":memory:"). Mutually exclusive with motherduck\_account. |
| motherduck\_account | [ string](#string) | MotherDuck account name. Requires motherduck\_token. |
| motherduck\_token | [ string](#string) | MotherDuck authentication token. Required when motherduck\_account is set. |
### FabricConf
(synq.agent.dwh.v1.FabricConf)
Microsoft Fabric Warehouse / Lakehouse SQL analytics endpoint configuration.
Fabric speaks T-SQL over TDS (like SQL Server) but is deliberately its own
integration with a minimal, opinionated surface: the connection always uses
TLS on port 1433 and authenticates with a Microsoft Entra ID service principal
(Fabric rejects SQL logins entirely). Callers therefore only supply the
endpoint, the warehouse name, and the service-principal credentials — the
azuread ActiveDirectoryServicePrincipal workflow and encryption settings are
fixed internally so there is nothing to guess.
For hosted deployments that mint their own Entra token (managed identity /
workload-identity federation), set access\_token instead of client\_id +
client\_secret.
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Workspace SQL analytics endpoint host, e.g. "\.datawarehouse.fabric.microsoft.com". |
| database | [ string](#string) | Default execution database — the context in which *unqualified* ad-hoc / monitor SQL resolves. Optional: defaults to "master" (the always-present workspace entry point). Metadata scrapping and generated metrics SQL are fully database-qualified via cross-database queries, so this only affects unqualified queries. It is a different axis from `databases` (which databases to scrape). |
| scope | [ ScopeFilter](#scopefilter) | Include/exclude filter over the workspace's databases/schemas/tables (ScopeRule.database = Fabric database/warehouse, .schema = schema, .table = table). The canonical scope encoding (as AthenaConf uses); unset means the whole workspace. A per-call scope can only narrow within this, never escape it. |
| client\_id | [ string](#string) | Entra application (client) ID of the service principal (default auth), or the user-assigned identity client ID when auth\_type is "managed\_identity". |
| client\_secret | [ string](#string) | Service principal client secret. |
| tenant\_id | [ string](#string) | Entra tenant (directory) ID. Optional: inferred from the endpoint when empty; set it when the server-supplied authority is not the SP's home tenant. |
| access\_token | [ string](#string) | Pre-acquired Entra OAuth access token for the SQL scope ([https://database.windows.net/.default](https://database.windows.net/.default)). When set, takes precedence over all other authentication methods — for managed identity / workload-identity hosts. |
| auth\_type | [ string](#string) | Authentication method, matched case-insensitively. Empty defaults to a service principal (client\_id + client\_secret). The ambient modes below authenticate as the host's own Azure identity with no stored credential and are intended for on-prem agents (opt-in — never engaged unless named): - "azure\_cli": reuse an interactive `az login` session (local execution) - "default": DefaultAzureCredential chain (managed identity → env → workload identity → az CLI) - "managed\_identity": Azure managed identity (set client\_id for a user-assigned identity) Canonical values are lower-case (the scrapper matches case-insensitively). |
### MSSQLConf
(synq.agent.dwh.v1.MSSQLConf)
Microsoft SQL Server / Azure SQL Database specific configuration.
SQL Server is database-scoped — each connection targets a single database.
Authentication methods (in priority order):
1. access\_token: pre-acquired Azure AD OAuth token
2. fed\_auth: Azure AD federated authentication (with optional username/password)
3. username + password: standard SQL Server Authentication
4. integrated\_auth: the Windows or Kerberos identity of the machine running the connection
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address or IP. For Azure SQL: e.g. "yourserver.database.windows.net" |
| port | [ int32](#int32) | Port number (default: 1433) |
| database | [ string](#string) | Database name to connect to |
| username | [ string](#string) | Username for SQL Server Authentication or Azure AD. For Azure AD Service Principal, this is the Application (Client) ID. Leave empty when using access\_token or Azure AD Managed Identity. |
| password | [ string](#string) | Password for SQL Server Authentication or Azure AD client secret |
| trust\_cert | [ bool](#bool) | Skip TLS server certificate verification |
| encrypt | [ string](#string) | Connection encryption mode: "true" (default), "false", "disable" |
| fed\_auth | [ string](#string) | Azure AD federated authentication method. When set, the azuread driver is used instead of the standard sqlserver driver. Values: "ActiveDirectoryDefault", "ActiveDirectoryMSI", "ActiveDirectoryServicePrincipal", "ActiveDirectoryAzCli" |
| access\_token | [ string](#string) | Pre-acquired Azure AD OAuth access token. When set, takes precedence over all other authentication methods. |
| application\_client\_id | [ string](#string) | Azure AD Application (Client) ID. Used with ActiveDirectoryServicePrincipal and user-assigned MSI. |
| integrated\_auth | [ bool](#bool) | Sign in with the identity of the user running the connection instead of a SQL Server login, the same as `sqlcmd -E` (Windows Authentication). On Windows this uses the logged-in user's credentials (SSPI). On macOS and Linux it uses the Kerberos ticket from `kinit`: the credential cache named by KRB5CCNAME (a file), or /tmp/krb5cc\_\, with the Kerberos configuration from KRB5\_CONFIG or /etc/krb5.conf. Leave username, password, access\_token and fed\_auth empty. |
### MySQLConf
(synq.agent.dwh.v1.MySQLConf)
MySQL specific configuration
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address |
| port | [ int32](#int32) | Port number (default: 3306) |
| database | [ string](#string) | Database name |
| username | [ string](#string) | Username for authentication |
| password | [ string](#string) | Password for authentication |
| allow\_insecure | [ bool](#bool) | Whether to allow insecure connections |
| params | [map ParamsEntry](#paramsentry) | Additional connection parameters |
### MySQLConf.ParamsEntry
(synq.agent.dwh.v1.MySQLConf.ParamsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### OracleConf
(synq.agent.dwh.v1.OracleConf)
Oracle Database specific configuration.
Oracle connections are scoped to a Pluggable Database (PDB) via the service name.
Authentication methods:
1. wallet\_path: Oracle Wallet (mTLS) for OCI Autonomous Database
2. username + password: standard Oracle database authentication
Both can be combined (wallet for TLS + explicit credentials).
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address or IP. For OCI: e.g. "adb.eu-frankfurt-1.oraclecloud.com" |
| port | [ int32](#int32) | Listener port (default: 1521, OCI Autonomous DB with mTLS uses 1522) |
| service\_name | [ string](#string) | Service name (PDB name). For OCI Autonomous DB: e.g. "mydb\_high", "mydb\_low", "mydb\_tp" |
| username | [ string](#string) | Username for authentication (optional when wallet stores credentials) |
| password | [ string](#string) | Password for authentication (optional when wallet stores credentials) |
| ssl | [ bool](#bool) | Enable TLS/SSL encryption (TCPS protocol). Required for OCI Autonomous Database. Auto-enabled when wallet\_path is set. |
| ssl\_verify | [ bool](#bool) | Enable server certificate verification when SSL is enabled |
| wallet\_path | [ string](#string) | Path to Oracle Wallet directory for mTLS authentication (OCI Autonomous DB). Contains TLS certificates (cwallet.sso) and optionally stored credentials. |
| use\_diagnostics\_pack | [ bool](#bool) | Use Oracle Diagnostics Pack (AWR) for persistent query log history. When false, query logs come from V\$SQL (in-memory, no additional license). When true, query logs come from DBA\_HIST views (requires Diagnostics Pack license). |
### PostgresConf
(synq.agent.dwh.v1.PostgresConf)
Postgres specific configuration
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address |
| port | [ int32](#int32) | Port number (default: 5432) |
| database | [ string](#string) | Database name |
| username | [ string](#string) | Username for authentication |
| password | [ string](#string) | Password for authentication |
| allow\_insecure | [ bool](#bool) | Whether to allow insecure connections |
### RedshiftConf
(synq.agent.dwh.v1.RedshiftConf)
Redshift specific configuration
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address |
| port | [ int32](#int32) | Port number (default: 5439) |
| database | [ string](#string) | Database name |
| username | [ string](#string) | Username for authentication |
| password | [ string](#string) | Password for authentication |
| freshness\_from\_query\_logs | [ bool](#bool) | Estimate table freshness based on query logs |
### SnowflakeConf
(synq.agent.dwh.v1.SnowflakeConf)
Snowflake specific configuration
| Field | Type | Description |
| - | - | - |
| account | [ string](#string) | Snowflake account identifier |
| warehouse | [ string](#string) | Virtual warehouse to use |
| role | [ string](#string) | Role to assume |
| username | [ string](#string) | Username for authentication |
| password | [ string](#string) | Password for authentication |
| private\_key | [ string](#string) | Content of Private key used for Snowflake authentication |
| private\_key\_file | [ string](#string) | Location of the file containing Private key used for Snowflake authentication |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_private\_key\_passphrase.private\_key\_passphrase | [optional string](#string) | Passphrase used to decode Private key |
| databases | [repeated string](#string) | Database to connect to |
| use\_get\_ddl | [ bool](#bool) | Use GET\_DDL to determine queries used for table/view creation |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_account\_usage\_db.account\_usage\_db | [optional string](#string) | Name of the database where ACCOUNT\_USAGE schema is present, fallbacks to SNOWFLAKE |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_auth\_type.auth\_type | [optional string](#string) | Authentication type: empty (default, uses password or private\_key), "externalbrowser" (SSO via browser) When set to "externalbrowser", opens browser for SSO login and caches the token locally. |
### TrinoConf
(synq.agent.dwh.v1.TrinoConf)
Trino specific configuration
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host address |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_port.port | [optional int32](#int32) | Optional port to use, otherwise it will use the default port 8080 |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_use\_plaintext.use\_plaintext | [optional bool](#bool) | Use non-SSL connection to Trino. This should only be enabled if the Trino cluster does not support SSL or if the connection is secured through other means (e.g., a VPN). Defaults to false (SSL enabled). |
| username | [ string](#string) | Username for authentication |
| password | [ string](#string) | Password for authentication |
| catalogs | [repeated string](#string) | To which catalogs to connect |
| no\_show\_create\_view | [ bool](#bool) | Use SHOW CREATE VIEW to get views DDLs |
| no\_show\_create\_table | [ bool](#bool) | Use SHOW CREATE TABLE to get tables DDLs |
| no\_materialized\_views | [ bool](#bool) | Should it fetch system.metadata.materialized\_views to get information about Trino MVs |
| fetch\_table\_comments | [ bool](#bool) | Fetch Trino table comments from system.metadata.table\_comments |
### Config
(synq.agent.dwh.v1.Config)
Config represents the main configuration for the DWH agent
| Field | Type | Description |
| - | - | - |
| agent | [ Agent](#agent) | Agent configuration |
| synq | [ SYNQ](#synq) | Coalesce Quality platform configuration |
| connections | [map ConnectionsEntry](#connectionsentry) | Map of connection configurations |
### Config.Agent
(synq.agent.dwh.v1.Config.Agent)
Agent contains metadata about this agent instance
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the agent instance |
| tags | [repeated string](#string) | Tags to categorize and organize the agent |
| log\_level | [ LogLevel](#loglevel) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_log\_json.log\_json | [optional bool](#bool) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_log\_report\_caller.log\_report\_caller | [optional bool](#bool) | none |
### Config.ConnectionsEntry
(synq.agent.dwh.v1.Config.ConnectionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Connection](#connection) | none |
### SYNQ
(synq.agent.v1.SYNQ)
SYNQ contains authentication and connection details for the Coalesce Quality platform
| Field | Type | Description |
| - | - | - |
| client\_id | [ string](#string) | Client ID for OAuth authentication |
| client\_secret | [ string](#string) | Client secret for OAuth authentication |
| endpoint | [ string](#string) | Coalesce Quality API agent endpoint (host:port) |
| ingest\_endpoint | [ string](#string) | Coalesce Quality API ingest endpoint (host:port) |
| oauth\_url | [ string](#string) | OAuth authentication URL |
### CutoffApplyConfig
(synq.agent.recon.v1.CutoffApplyConfig)
CutoffApplyConfig controls how the resolved cutoff value is applied as a WHERE filter.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_column.column | [optional string](#string) | Column to filter on. When not set, uses the derivation column from that side. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_operator.operator | [optional string](#string) | Comparison operator. Default: "≤". |
### CutoffConfig
(synq.agent.recon.v1.CutoffConfig)
CutoffConfig defines a dynamic cutoff filter for sync reconciliation.
It derives a watermark value from the actual data (e.g., MAX(created\_at))
to automatically exclude rows that haven't been synced yet.
The cutoff is resolved at runtime before comparison queries run:
1. Derive watermark(s) from source and/or target via aggregate queries.
2. Combine them (default: MIN of both sides).
3. Optionally truncate to a time boundary and apply an offset.
4. Apply as WHERE filter to both source and target queries.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source.source | [optional CutoffSideConfig](#cutoffsideconfig) | Per-side watermark derivation config for source. When not set, derives from target only. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_target.target | [optional CutoffSideConfig](#cutoffsideconfig) | Per-side watermark derivation config for target. When not set, derives from source only. |
| combine | [ CutoffCombineStrategy](#cutoffcombinestrategy) | How to combine watermarks when both sides are configured. Default: MIN (use the smaller watermark for safety). Ignored when only one side has a watermark. |
| truncate | [ CutoffTruncateUnit](#cutofftruncateunit) | Truncate the combined watermark to a time boundary. Example: HOUR truncates 08:47:12 to 08:00:00. Applied after combining, before offset. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_offset.offset | [optional Duration](#duration) | Time offset applied after truncation. Negative durations subtract from the cutoff (safety buffer). Example: "-30m" shifts the cutoff 30 minutes earlier. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_apply\_source.apply\_source | [optional CutoffApplyConfig](#cutoffapplyconfig) | How to apply the cutoff as a WHERE filter on the source side. When not set, uses the source derivation column with ≤ operator. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_apply\_target.apply\_target | [optional CutoffApplyConfig](#cutoffapplyconfig) | How to apply the cutoff as a WHERE filter on the target side. When not set, uses the target derivation column with ≤ operator. |
### CutoffSideConfig
(synq.agent.recon.v1.CutoffSideConfig)
CutoffSideConfig configures watermark derivation for one side (source or target).
There are two modes:
1. Column + aggregate (default): auto-generates a watermark query from the dataset.
For table-based datasets, queries the table directly (the column does NOT need
to be in the reconciliation column list). For query-based datasets, wraps the
query as a subquery (the column MUST be in the query's SELECT list).
2. Custom query: provide a SQL query that returns a single row with a "watermark" column.
Use this when the auto-generated query doesn't work (e.g., complex joins, custom logic).
After resolution at runtime, the `query` field is always populated with the actual SQL
that was executed, regardless of which mode was used. This ensures the audit log captures
the exact query for reproducibility.
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Column to derive the watermark from (e.g., "created\_at", "synced\_at"). Required when query is not set. |
| aggregate | [ CutoffAggregateFunction](#cutoffaggregatefunction) | Aggregate function to derive the watermark. Default: MAX. Only used when query is not set. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_query.query | [optional string](#string) | Custom SQL query for watermark derivation. Must return a single row with a "watermark" column. When not set, auto-generated from the dataset at runtime. After resolution, this field is populated with the actual query that was executed. |
### ParseYamlRequest
(synq.agent.recon.v1.ParseYamlRequest)
| Field | Type | Description |
| - | - | - |
| yaml | [ string](#string) | YAML config string to parse. |
### ParseYamlResponse
(synq.agent.recon.v1.ParseYamlResponse)
| Field | Type | Description |
| - | - | - |
| suite | [ ReconciliationSuite](#reconciliationsuite) | The parsed suite configuration. |
### RenderYamlRequest
(synq.agent.recon.v1.RenderYamlRequest)
| Field | Type | Description |
| - | - | - |
| suite | [ ReconciliationSuite](#reconciliationsuite) | The suite configuration to render as YAML. |
### RenderYamlResponse
(synq.agent.recon.v1.RenderYamlResponse)
| Field | Type | Description |
| - | - | - |
| yaml | [ string](#string) | The rendered YAML config string. |
### ValidateSuiteRequest
(synq.agent.recon.v1.ValidateSuiteRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) input.yaml | [ string](#string) | A suite in the YAML format of synq-recon. Each issue then has the position of its problem in the YAML text. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) input.suite | [ ReconciliationSuite](#reconciliationsuite) | A suite in the format that Coalesce Quality stores. The request does not apply the validation rules of ReconciliationSuite. Thus an incorrect suite gets to ValidateSuite, and the response has one issue for each rule that the suite does not obey. |
### ValidateSuiteResponse
(synq.agent.recon.v1.ValidateSuiteResponse)
| Field | Type | Description |
| - | - | - |
| valid | [ bool](#bool) | True when no issue has the ERROR severity. A suite with only warnings is valid. |
| issues | [repeated SuiteValidationIssue](#suitevalidationissue) | All the problems in the suite. The errors come first, then the warnings. Empty when the suite has no problems. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite.suite | [optional ReconciliationSuite](#reconciliationsuite) | The suite from the yaml input. It is not set when the request gives the suite as a proto, or when the YAML text does not give a suite. |
### ColumnThresholdOverride
(synq.agent.recon.v1.ColumnThresholdOverride)
ColumnThresholdOverride associates threshold overrides with a specific
group\_column level in the aggregate drill-down hierarchy.
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Group column name this override applies to. Must match one of the group\_columns in the AggregateConfig. |
| thresholds | [ ThresholdConfig](#thresholdconfig) | Threshold overrides for this group column level. Values here override the parent ThresholdConfig for reconciliation groups at this drill-down level. May include per\_measure for further specificity. |
### MeasureThresholdOverride
(synq.agent.recon.v1.MeasureThresholdOverride)
MeasureThresholdOverride associates threshold overrides with a specific
measure (aggregate function + column combination).
| Field | Type | Description |
| - | - | - |
| function | [ AggregateFunction](#aggregatefunction) | Aggregate function this override applies to. |
| column | [ string](#string) | Column name this override applies to. Together with function, forms the measure key (e.g., SUM + "amount" = "SUM(amount)"). |
| thresholds | [ ThresholdConfig](#thresholdconfig) | Threshold overrides for this specific measure. Values here override the parent ThresholdConfig (and any per\_column override) for this measure only. |
### ThresholdConfig
(synq.agent.recon.v1.ThresholdConfig)
ThresholdConfig defines tolerance thresholds for aggregate and row count comparisons.
A difference is reported as a mismatch only when it exceeds ALL configured
thresholds (AND logic: both absolute AND percentage must be exceeded).
Thresholds can be overridden at finer granularity:
* per\_column: override thresholds for a specific group\_column drill-down level.
* per\_measure: override thresholds for a specific measure (e.g., "SUM(amount)").
* Nesting: per\_column entries may contain per\_measure overrides (most specific wins).
When no thresholds are configured at any level, exact match is required.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_absolute.absolute | [optional double](#double) | Maximum allowed absolute difference per measure. When set, differences ≤ this value are considered within tolerance. Example: absolute = 0.01 accepts sub-cent rounding differences. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_percentage.percentage | [optional double](#double) | Maximum allowed percentage difference per measure. Expressed as a decimal fraction: 0.1 = 10%, 0.01 = 1%. The formula used depends on percentage\_basis. When set, differences ≤ this percentage are considered within tolerance. |
| percentage\_basis | [ PercentageBasis](#percentagebasis) | Which percentage formula to use for threshold evaluation. Only relevant when percentage is set. |
| per\_column | [repeated ColumnThresholdOverride](#columnthresholdoverride) | Per group\_column level threshold overrides. Allows different tolerance for different levels of the drill-down hierarchy. For example, tighter thresholds at the city level than at the region level. Entries may themselves contain per\_measure overrides for maximum specificity. |
| per\_measure | [repeated MeasureThresholdOverride](#measurethresholdoverride) | Per measure threshold overrides. Allows different tolerance for different measures within the same reconciliation. For example, allow 1% tolerance for SUM(amount) but require exact COUNT match. |
### AggregateDrillTarget
(synq.agent.recon.v1.AggregateDrillTarget)
AggregateDrillTarget specifies how to extend or drill deeper into aggregate results.
| Field | Type | Description |
| - | - | - |
| groups | [repeated GroupTarget](#grouptarget) | Specific group key values to drill into. When empty, all mismatched groups from the parent audit are drilled. |
| additional\_group\_columns | [repeated string](#string) | Additional group columns to add to the drill-down hierarchy. Appended after the existing group\_columns from the reconciliation config. |
| additional\_measures | [repeated Measure](#measure) | Additional measures to compute that weren't in the original config. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_threshold\_overrides.threshold\_overrides | [optional ThresholdConfig](#thresholdconfig) | Override thresholds for this drill pass. |
### BisectionDrillTarget
(synq.agent.recon.v1.BisectionDrillTarget)
BisectionDrillTarget specifies which mismatch leaves to drill deeper into.
| Field | Type | Description |
| - | - | - |
| segments | [repeated SegmentTarget](#segmenttarget) | Specific segments to drill into. Each segment is identified by its boundaries. When empty, all mismatch leaves from the parent audit are drilled. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_factor.factor | [optional int32](#int32) | Override the bisection factor for this drill pass. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_threshold.threshold | [optional int32](#int32) | Override the row count threshold for this drill pass. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_mismatch\_detail\_level.mismatch\_detail\_level | [optional MismatchDetailLevel](#mismatchdetaillevel) | Override the reporting level for this drill pass. |
### GroupTarget
(synq.agent.recon.v1.GroupTarget)
GroupTarget identifies a specific group to drill into by its key values.
| Field | Type | Description |
| - | - | - |
| key | [ Struct](#struct) | Group key values that identify this group (column name -> value). |
### ReconciliationFilter
(synq.agent.recon.v1.ReconciliationFilter)
ReconciliationFilter controls which reconciliations are included or excluded.
| Field | Type | Description |
| - | - | - |
| include | [repeated string](#string) | Run only these reconciliations (by name). Mutually exclusive with exclude. |
| exclude | [repeated string](#string) | Skip these reconciliations (by name). Mutually exclusive with include. |
### ReconciliationOverride
(synq.agent.recon.v1.ReconciliationOverride)
ReconciliationOverride provides per-reconciliation instructions for a run.
Used to drill into specific mismatches, adjust config, or extend comparisons.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the reconciliation this override applies to. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.bisection\_drill | [ BisectionDrillTarget](#bisectiondrilltarget) | Drill into specific bisection mismatch leaves from a prior run. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.aggregate\_drill | [ AggregateDrillTarget](#aggregatedrilltarget) | Drill into specific aggregate groups or add new dimensions from a prior run. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) action.rerun | [ RerunAction](#rerunaction) | Re-run the reconciliation from scratch, optionally with config changes. |
### ReconciliationRunRequest
(synq.agent.recon.v1.ReconciliationRunRequest)
ReconciliationRunRequest describes what to execute in a reconciliation run.
It can start a fresh run, continue from a previous audit, or drill deeper
into specific mismatches found in a prior run.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite.suite | [optional ReconciliationSuite](#reconciliationsuite) | Suite to execute. Required for fresh runs. When continuing from a parent audit, can be omitted to reuse the parent's suite. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_audit.parent\_audit | [optional AuditLog](#auditlog) | Parent audit log to continue from. When set, this run resumes or drills deeper into the parent's results. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_filter.filter | [optional ReconciliationFilter](#reconciliationfilter) | Filter which reconciliations to execute. When empty, all reconciliations in the suite are executed. |
| variable\_overrides | [map VariableOverridesEntry](#variableoverridesentry) | Variable overrides applied on top of suite-level variables. |
| overrides | [repeated ReconciliationOverride](#reconciliationoverride) | Per-reconciliation drill-down and override instructions. |
### ReconciliationRunRequest.VariableOverridesEntry
(synq.agent.recon.v1.ReconciliationRunRequest.VariableOverridesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### RerunAction
(synq.agent.recon.v1.RerunAction)
RerunAction re-runs a reconciliation from scratch with optional config overrides.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_mode.mode | [optional ReconciliationMode](#reconciliationmode) | Override the comparison mode. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bisection.bisection | [optional BisectionConfig](#bisectionconfig) | Override the bisection configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reporting.reporting | [optional ReportingConfig](#reportingconfig) | Override the reporting configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_aggregate.aggregate | [optional AggregateConfig](#aggregateconfig) | Override the aggregate configuration. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_error\_handling.error\_handling | [optional ErrorHandlingConfig](#errorhandlingconfig) | Override the error handling configuration. |
### SegmentTarget
(synq.agent.recon.v1.SegmentTarget)
SegmentTarget identifies a specific segment to drill into.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_min\_key.min\_key | [optional Value](#value) | Minimum key (inclusive). Unset means unbounded. |
The encoding matches SegmentInfo in the audit log, and is deliberately not self-describing: a numeric key appears as a JSON number when it fits the exact double range and as a JSON string above it, while a textual key always appears as a JSON string. A composite key is a JSON list, one element per key column. Only the key column's own type distinguishes the cases, so the runner types these bounds after resolving the query shape. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_max\_key.max\_key | [optional Value](#value) | Maximum key (exclusive). Unset means unbounded. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_time\_bucket.time\_bucket | [optional string](#string) | Time bucket (for time-based segmentation). |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_seed\_depth.seed\_depth | [optional int32](#int32) | Depth this segment sat at in the run being resumed.
Carried so a resumed drill reports true absolute depth and can budget its own depth limit as "this many further levels" rather than re-spending a budget the previous run already consumed. |
### DeleteSuiteConfigRequest
(synq.agent.recon.v1.DeleteSuiteConfigRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Suite ID to delete. |
| actor | [ Actor](#actor) | Actor performing the delete. Required. |
### DeleteSuiteConfigResponse
(synq.agent.recon.v1.DeleteSuiteConfigResponse)
### GetSuiteConfigRequest
(synq.agent.recon.v1.GetSuiteConfigRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Suite ID to look up. For managed suites this equals the suite name, for ad-hoc suites this is the generated UUID. |
### GetSuiteConfigResponse
(synq.agent.recon.v1.GetSuiteConfigResponse)
| Field | Type | Description |
| - | - | - |
| suite\_config | [ SuiteConfig](#suiteconfig) | none |
### GetSuiteConfigVersionRequest
(synq.agent.recon.v1.GetSuiteConfigVersionRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Suite ID. |
| state\_at | [ Timestamp](#timestamp) | Version timestamp. |
### GetSuiteConfigVersionResponse
(synq.agent.recon.v1.GetSuiteConfigVersionResponse)
| Field | Type | Description |
| - | - | - |
| version | [ SuiteConfigVersion](#suiteconfigversion) | none |
### ListSuiteConfigVersionsRequest
(synq.agent.recon.v1.ListSuiteConfigVersionsRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Suite ID to get version history for. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination parameters. |
### ListSuiteConfigVersionsResponse
(synq.agent.recon.v1.ListSuiteConfigVersionsResponse)
| Field | Type | Description |
| - | - | - |
| versions | [repeated SuiteConfigVersion](#suiteconfigversion) | none |
| page\_info | [ PageInfo](#pageinfo) | Pagination info for fetching the next page. |
### ListSuiteConfigsRequest
(synq.agent.recon.v1.ListSuiteConfigsRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_connection\_name.connection\_name | [optional string](#string) | Filter suites using a specific connection name. When empty, returns all. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination parameters. |
| include\_adhoc | [ bool](#bool) | When true, also include ad-hoc suite configs in the results. By default only managed suites are returned. |
### ListSuiteConfigsResponse
(synq.agent.recon.v1.ListSuiteConfigsResponse)
| Field | Type | Description |
| - | - | - |
| suite\_configs | [repeated SuiteConfig](#suiteconfig) | none |
| page\_info | [ PageInfo](#pageinfo) | Pagination info for fetching the next page. |
### ResumeFromRun
(synq.agent.recon.v1.ResumeFromRun)
ResumeFromRun turns an ad-hoc submission into a replay of a previous run:
either a re-validation ("do the differences it found still exist") or a
deeper drill of the mismatches it stopped at.
The suite comes from the parent run's audit log, not from a config load, so
the replay compares the same data the parent compared — template variables
keep the values they resolved to and a cutoff keeps the watermark it derived.
Credentials never come from an audit log: it records connection names only,
and those are bound to integrations exactly as for any other ad-hoc run
(connection\_mappings, else a workspace integration of the same name).
| Field | Type | Description |
| - | - | - |
| parent\_invocation\_id | [ string](#string) | Invocation id of the run to continue from. It must be a completed run in the caller's workspace whose audit log carries a suite snapshot. |
| mode | [ ResumeMode](#resumemode) | What to do with the parent's results. |
| reconciliations | [repeated string](#string) | Restrict the replay to these reconciliation names. Empty replays every reconciliation of the parent that is eligible for the mode. |
| reresolve | [ bool](#bool) | Re-derive every query from the suite instead of replaying the SQL the parent executed. This moves the comparison window to the current clock, so it answers "does the suite pass now" rather than "is that difference still there". Needed for a time-travel snapshot, which can otherwise never turn green. |
| include\_passed | [ bool](#bool) | RESUME\_MODE\_RECHECK only: re-run reconciliations that already passed. The default narrows to the ones that did not, which is the point of a re-validation. |
| drill\_still\_mismatched | [ bool](#bool) | RESUME\_MODE\_RECHECK only: drill into whatever still mismatches, as an ordinary run with run\_bisection does. A re-validation usually only needs the quick check, so this is opt-in. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_threshold.threshold | [optional int32](#int32) | RESUME\_MODE\_DRILL\_DEEPER only: row count at which drilling stops, overriding the reconciliation's own. Lowering it is the usual reason to resume a bisection drill — the parent stopped at a coarser threshold. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_extra\_depth.extra\_depth | [optional int32](#int32) | RESUME\_MODE\_DRILL\_DEEPER only: how many further levels to drill below the resumed segments. Counted from the deepest resumed segment, so it means "this much deeper" rather than a budget the parent already spent. Absent leaves it unbounded. |
| additional\_group\_columns | [repeated string](#string) | RESUME\_MODE\_DRILL\_DEEPER only: extra group columns to break each divergent aggregate group down by. An aggregate drill already visits every configured group column, so resuming an aggregate reconciliation needs at least one more column to make progress. |
### SubmitAdhocSuiteRequest
(synq.agent.recon.v1.SubmitAdhocSuiteRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite.suite | [optional ReconciliationSuite](#reconciliationsuite) | Inline suite configuration. Required when neither suite\_id nor resume\_from\_run is set. |
| invocation\_id | [ string](#string) | Client-generated invocation ID for idempotency. If an execution with this ID already exists, the call is a no-op. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_id.suite\_id | [optional string](#string) | Reference to an existing managed suite (alternative to inline suite). When set, the suite config is loaded from the database. |
| connection\_mappings | [repeated ConnectionMapping](#connectionmapping) | Maps each connection name referenced by the suite to the integration that should provide credentials at execution time. |
When omitted, the server attempts to resolve each connection name against a workspace integration of the same name (legacy behaviour). When the suite references connection names that the server cannot resolve, the call fails with FAILED\_PRECONDITION + a ConnectionResolutionFailure detail listing every unresolved name and candidate integrations, so the caller can prompt the user to bind them.
Both workspace-level (integration\_id) and user-level (user\_integration\_id) credentials are accepted here: - integration\_id requires SCOPE\_INTEGRATION\_EDIT on the caller. - user\_integration\_id requires a user identity in the calling context and the integration's allow\_reconciliation\_adhoc gate enabled. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_bisection.run\_bisection | [optional bool](#bool) | When true (default), the runner drills into each mismatched reconciliation via bisection after the quick check fails. Set to false to stop after the quick check — useful for cheap smoke runs, or when the caller plans to trigger a manual drill later.
Per-reconciliation `bisection.enabled` still gates whether a given recon is eligible for drill, so this flag is the run-level off switch layered on top. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_timeout.execution\_timeout | [optional Duration](#duration) | Maximum wall-clock time for this ad-hoc run (quick check plus any bisection drill). A run that exceeds it is cancelled and marked failed. Absent uses the server default; the server clamps the effective value to a supported range. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_resume\_from\_run.resume\_from\_run | [optional ResumeFromRun](#resumefromrun) | Continue a previous run instead of executing a suite from its config. Mutually compatible with neither suite nor suite\_id being set: the suite is rebuilt from the parent run's audit log. |
\| actor | [ Actor](#actor) | Actor submitting the run. Required. |
### SubmitAdhocSuiteResponse
(synq.agent.recon.v1.SubmitAdhocSuiteResponse)
| Field | Type | Description |
| - | - | - |
| suite\_config | [ SuiteConfig](#suiteconfig) | The stored suite config (includes the invocation\_id as suite\_id). |
### SuiteConfig
(synq.agent.recon.v1.SuiteConfig)
SuiteConfig represents the latest version of a reconciliation suite configuration.
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Unique identifier. Equals suite\_name for managed suites, UUID for ad-hoc. |
| suite\_name | [ string](#string) | Human-readable suite name from ReconciliationSuite.name. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_title.suite\_title | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_description.suite\_description | [optional string](#string) | none |
| suite\_type | [ string](#string) | "managed" or "adhoc". |
| suite | [ ReconciliationSuite](#reconciliationsuite) | The full suite configuration. |
| connection\_names | [repeated string](#string) | Deduplicated connection names referenced by this suite. |
| reconciliation\_summaries | [repeated ReconciliationConfigSummary](#reconciliationconfigsummary) | Summary of each reconciliation in the suite. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_updated\_by.updated\_by | [optional Actor](#actor) | Who last updated this config. |
`updated_by.via` tells where the save came from. It is VIA\_API for a save through the public API, for example from `synq-recon upload-config`. It is VIA\_UNSPECIFIED for a save in the Coalesce Quality app. |
\| state\_at | [ Timestamp](#timestamp) | When this version was created. To save an edit of this version only if no other save changed the suite, send this value as UpsertSuiteConfigRequest.expected\_state\_at. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | The summary of the change that the save gave. It is not set when the save gave no summary. |
### SuiteConfigConflict
(synq.agent.recon.v1.SuiteConfigConflict)
The error details of the FAILED\_PRECONDITION error that an
UpsertSuiteConfigRequest gets when expected\_state\_at is not the latest
version of the suite.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_current.current | [optional SuiteConfig](#suiteconfig) | The latest version of the suite. It is not set when the suite was deleted. |
### SuiteConfigVersion
(synq.agent.recon.v1.SuiteConfigVersion)
SuiteConfigVersion represents a historical version of a suite config.
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| suite\_name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_title.suite\_title | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_description.suite\_description | [optional string](#string) | none |
| suite\_type | [ string](#string) | none |
| suite | [ ReconciliationSuite](#reconciliationsuite) | The full suite configuration at this version. |
| connection\_names | [repeated string](#string) | Connection names at this version. |
| reconciliation\_summaries | [repeated ReconciliationConfigSummary](#reconciliationconfigsummary) | Reconciliation summaries at this version. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_updated\_by.updated\_by | [optional Actor](#actor) | Who made this change. |
| state\_at | [ Timestamp](#timestamp) | When this version was created. |
| deleted | [ bool](#bool) | Whether this version represents a deletion. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | Human-readable summary of what changed. |
### UpsertSuiteConfigRequest
(synq.agent.recon.v1.UpsertSuiteConfigRequest)
| Field | Type | Description |
| - | - | - |
| suite | [ ReconciliationSuite](#reconciliationsuite) | The suite configuration to store. suite\_id will equal suite.name. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | Human-readable summary of what changed in this version. |
| actor | [ Actor](#actor) | Actor performing the upsert. Required. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_expected\_state\_at.expected\_state\_at | [optional Timestamp](#timestamp) | Save the suite only if this is still its latest version. Set it to the `state_at` of the SuiteConfig that the edit started from. |
If a different save changed the suite after that version, or if someone deleted the suite, the request does not save. It gets FAILED\_PRECONDITION with a SuiteConfigConflict detail, which has the latest version.
If you do not set it, the save replaces the latest version. |
\| create\_only | [ bool](#bool) | Save the suite only if no suite has this name. This prevents an upload that replaces a suite by accident. If the name is in use, the request gets ALREADY\_EXISTS. You can use the name of a deleted suite. |
### UpsertSuiteConfigResponse
(synq.agent.recon.v1.UpsertSuiteConfigResponse)
| Field | Type | Description |
| - | - | - |
| suite\_config | [ SuiteConfig](#suiteconfig) | The stored suite config. |
### CronSchedule
(synq.agent.recon.v1.CronSchedule)
| Field | Type | Description |
| - | - | - |
| expression | [ string](#string) | Standard cron expression (e.g. "0 \*/6 \* \* \*"). |
### ICalSchedule
(synq.agent.recon.v1.ICalSchedule)
| Field | Type | Description |
| - | - | - |
| recurrence\_rule | [ string](#string) | RFC 5545 RRULE recurrence rule (e.g. "FREQ=MONTHLY;BYDAY=TH;BYSETPOS=-1"). DTSTART should be set via the dtstart field below rather than inline in the rule. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_dtstart.dtstart | [optional Timestamp](#timestamp) | Anchor time for the recurrence rule. Wall-clock time interpreted in the schedule's timezone. Defines when the schedule starts and aligns occurrences. If not set and no DTSTART is present in the recurrence\_rule, defaults to now(). |
### ReconciliationConfigSummary
(synq.agent.recon.v1.ReconciliationConfigSummary)
Summary of a single reconciliation within a suite config (for listing without parsing full config).
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | none |
| source\_connection | [ string](#string) | none |
| target\_connection | [ string](#string) | none |
| mode | [ ReconciliationMode](#reconciliationmode) | none |
| annotations | [repeated Annotation](#annotation) | Resolved annotation list for this case. For suite-config summaries this is suite-level annotations + case-level annotations (deduped). For deployment summaries the deployment-level overlay is also merged in. Already deduplicated by (name, value); ready for direct display. |
### SuiteSchedule
(synq.agent.recon.v1.SuiteSchedule)
Schedule configuration for managed suites.
| Field | Type | Description |
| - | - | - |
| timezone | [ string](#string) | IANA timezone, e.g. "Europe/Warsaw". Required when a schedule is set. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.cron | [ CronSchedule](#cronschedule) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.ical | [ ICalSchedule](#icalschedule) | none |
### AuditLogEntry
(synq.agent.recon.v1.AuditLogEntry)
AuditLogEntry is a summary of an audit log for listing purposes.
Use GetAuditLog to retrieve the full audit log data.
| Field | Type | Description |
| - | - | - |
| invocation\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_invocation\_id.parent\_invocation\_id | [optional string](#string) | none |
| suite\_name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_title.suite\_title | [optional string](#string) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| completed\_at | [ Timestamp](#timestamp) | none |
| duration | [ Duration](#duration) | none |
| status | [ AuditOutcome](#auditoutcome) | none |
| summary | [ AuditSummary](#auditsummary) | none |
| reconciliations | [repeated ReconciliationSummary](#reconciliationsummary) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_id.suite\_id | [optional string](#string) | Stable suite identifier assigned by the platform — same value as the suite\_id on RunStateService.ReconciliationRunState, suitable for joining a run row to its audit log. Empty for legacy logs ingested before run-state correlation was added. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deployment\_id.deployment\_id | [optional string](#string) | Deployment identifier this run belongs to (UUIDv7). Empty for adhoc submissions and for legacy logs ingested before run-state correlation. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_id.run\_id | [optional string](#string) | Stable run identifier (UUIDv7) assigned at scheduling time. Empty for legacy logs / direct agent submissions made outside the platform job queue. |
### GetAuditLogRequest
(synq.agent.recon.v1.GetAuditLogRequest)
| Field | Type | Description |
| - | - | - |
| invocation\_id | [ string](#string) | Invocation ID of the audit log to retrieve. |
### GetAuditLogResponse
(synq.agent.recon.v1.GetAuditLogResponse)
| Field | Type | Description |
| - | - | - |
| audit\_log | [ AuditLog](#auditlog) | none |
### IngestAuditLogRequest
(synq.agent.recon.v1.IngestAuditLogRequest)
| Field | Type | Description |
| - | - | - |
| audit\_log | [ AuditLog](#auditlog) | The audit log to store. |
### IngestAuditLogResponse
(synq.agent.recon.v1.IngestAuditLogResponse)
| Field | Type | Description |
| - | - | - |
| messages | [repeated IngestMessage](#ingestmessage) | Messages for the user who reported the run, such as a warning about part of the audit log that was not stored, or a link to the run in the app. Show each one as it is. Empty when there is nothing to say. |
### IngestMessage
(synq.agent.recon.v1.IngestMessage)
IngestMessage is something to tell the user who reported a run.
| Field | Type | Description |
| - | - | - |
| severity | [ IngestMessageSeverity](#ingestmessageseverity) | How prominently to show the message. |
| text | [ string](#string) | The message, as one or more plain sentences. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_url.url | [optional string](#string) | A link that goes with the message, for example to the run in the app. |
### ListAuditLogsRequest
(synq.agent.recon.v1.ListAuditLogsRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_name.suite\_name | [optional string](#string) | Filter by suite name. When empty, returns all suites. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_started\_after.started\_after | [optional Timestamp](#timestamp) | Only return audit logs started after this timestamp (inclusive). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_started\_before.started\_before | [optional Timestamp](#timestamp) | Only return audit logs started before this timestamp (exclusive). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination parameters. |
| statuses | [repeated AuditOutcome](#auditoutcome) | Filter by audit status. When empty, returns all statuses. |
### ListAuditLogsResponse
(synq.agent.recon.v1.ListAuditLogsResponse)
| Field | Type | Description |
| - | - | - |
| audit\_logs | [repeated AuditLogEntry](#auditlogentry) | none |
| page\_info | [ PageInfo](#pageinfo) | Pagination info for fetching the next page. |
### ReconciliationSummary
(synq.agent.recon.v1.ReconciliationSummary)
ReconciliationSummary is a compact summary of a single reconciliation within an audit log entry.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | none |
| source\_connection | [ string](#string) | Source connection name. |
| target\_connection | [ string](#string) | Target connection name. |
| status | [ ReconciliationOutcome](#reconciliationoutcome) | none |
| overall\_match | [ bool](#bool) | none |
| error | [ string](#string) | Error message if the reconciliation failed. |
### ReconciliationRunState
(synq.agent.recon.v1.ReconciliationRunState)
ReconciliationRunState describes the lifecycle of a single reconciliation
run. Returned by RunStateService.
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Suite identifier this run belongs to. For managed suites this is the suite\_name; for ad-hoc suites this is the generated UUID. |
| deployment\_id | [ string](#string) | Deployment identifier (UUIDv7) if the run belongs to a promoted deployment. Empty for ad-hoc and legacy managed-suite runs. |
| run\_id | [ string](#string) | Stable opaque identifier of this specific run occurrence (UUIDv7). |
| status | [ ReconciliationRunStatus](#reconciliationrunstatus) | none |
| trigger | [ ReconciliationRunTrigger](#reconciliationruntrigger) | none |
| scheduled\_at | [ Timestamp](#timestamp) | When this run was scheduled to start (cron occurrence time, or now() for ad-hoc). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_started\_at.started\_at | [optional Timestamp](#timestamp) | When the worker actually picked up the run. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_finished\_at.finished\_at | [optional Timestamp](#timestamp) | When the run reached a terminal status (SUCCEEDED / FAILED / CANCELLED). |
| last\_updated\_at | [ Timestamp](#timestamp) | Last time this row was updated. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Actor that initiated the run (user or system). Populated for ADHOC and API triggers. |
| failure\_category | [ ReconciliationRunFailureCategory](#reconciliationrunfailurecategory) | Set only when status is FAILED. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_failure\_details.failure\_details | [optional string](#string) | Raw failure detail string. Set only when status is FAILED. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_invocation\_id.invocation\_id | [optional string](#string) | Identifier of the audit log this run produced. Empty when the run failed before the worker emitted an audit log (e.g. binding-resolution failure on a misconfigured deployment, or the run was cancelled). When set, pass to AuditLogService.GetAuditLog to retrieve the full per-reconciliation results for this run. |
### SuiteValidationFailure
(synq.agent.recon.v1.SuiteValidationFailure)
The error details of an INVALID\_ARGUMENT error that a request gets because
of the contents of its suite. It lists all issues with the ERROR severity,
so that the caller can show each issue next to its field.
| Field | Type | Description |
| - | - | - |
| issues | [repeated SuiteValidationIssue](#suitevalidationissue) | none |
### SuiteValidationIssue
(synq.agent.recon.v1.SuiteValidationIssue)
One problem in a reconciliation suite.
| Field | Type | Description |
| - | - | - |
| severity | [ SuiteValidationSeverity](#suitevalidationseverity) | none |
| field\_path | [ string](#string) | The field that has the problem. The path uses the field names of ReconciliationSuite, for example `reconciliations[2].comparison.columns[0].numeric_scale` or `execution.timeout`. The index of a list item is its position in the suite. |
A field that only YAML has uses its YAML key, for example `reconciliations[0].setup_file`.
The path is empty when the problem is about the full suite, or when the YAML text is not valid YAML. |
\| message | [ string](#string) | A description of the problem for the person who edits the suite. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_position.position | [optional YamlPosition](#yamlposition) | The position of the problem in the YAML text. It is set only when the request gives the suite as YAML.
It points to the key of the field in field\_path. If the YAML does not have that key, for example because a required field is missing, it points to the nearest key that contains the field. |
### YamlPosition
(synq.agent.recon.v1.YamlPosition)
A line and a column in a YAML document. Both start at 1.
| Field | Type | Description |
| - | - | - |
| line | [ int32](#int32) | none |
| column | [ int32](#int32) | none |
### CaseCategories
(synq.agent.recon.v1.CaseCategories)
CaseCategories declares the categories for a single reconciliation case,
overriding what the deployment declares. The two dimensions are independent, so
a case can override one and inherit the other.
Per field:
* absent : inherit the deployment's declaration
* present, empty string : declare nothing for this case, even when the
deployment declares a value — the category then
falls to the reserved annotation or the workspace's
categorisation rules
* present, non-empty : set this case's category
An entry with neither field set therefore means the same as no entry at all, and
is dropped rather than stored.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | none |
### ConnectionMapping
(synq.agent.recon.v1.ConnectionMapping)
Maps a reconciliation-connection name (as referenced in the suite config)
to a concrete SYNQ integration. Used at execution time to resolve DWH
credentials.
Two credential planes are defined on the wire:
* integration\_id: workspace-level integration. Required for deployment
paths and acceptable for adhoc submission when the caller has
SCOPE\_INTEGRATION\_EDIT.
* user\_integration\_id: per-user OAuth-backed integration. Accepted
only on adhoc submission, and only when a user identity is present
in the calling context. Deployment paths reject this branch
unconditionally — scheduled runs must be able to fire without any
particular user being available, so they always run with workspace
credentials.
Wire-compatible with the previous shape: the original `integration_id`
(field 2) is preserved as one branch of the oneof so existing serialized
rows decode unchanged.
| Field | Type | Description |
| - | - | - |
| connection\_name | [ string](#string) | Connection name as it appears in the suite config (e.g. ReconciliationSuite.reconciliations\[].source.connection). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) source.integration\_id | [ string](#string) | Workspace-level integration (credentials owned by the workspace). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) source.user\_integration\_id | [ string](#string) | Per-user integration ID (user-level credentials). Server requires a user identity in the calling context to accept this branch. |
### ConnectionResolutionFailure
(synq.agent.recon.v1.ConnectionResolutionFailure)
Error-details payload attached to FAILED\_PRECONDITION when a recon
request cannot be honoured because one or more suite connection names
have no usable mapping. The caller should prompt the user to bind every
listed connection and retry.
| Field | Type | Description |
| - | - | - |
| unresolved | [repeated UnresolvedConnection](#unresolvedconnection) | none |
### GetSuiteDeploymentRequest
(synq.agent.recon.v1.GetSuiteDeploymentRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) lookup.suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) lookup.deployment\_id | [ string](#string) | none |
### GetSuiteDeploymentResponse
(synq.agent.recon.v1.GetSuiteDeploymentResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### ListSuiteDeploymentHistoryRequest
(synq.agent.recon.v1.ListSuiteDeploymentHistoryRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) lookup.suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) lookup.deployment\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | none |
### ListSuiteDeploymentHistoryResponse
(synq.agent.recon.v1.ListSuiteDeploymentHistoryResponse)
| Field | Type | Description |
| - | - | - |
| entries | [repeated SuiteDeploymentHistoryEntry](#suitedeploymenthistoryentry) | none |
| page\_info | [ PageInfo](#pageinfo) | none |
### ListSuiteDeploymentsRequest
(synq.agent.recon.v1.ListSuiteDeploymentsRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_integration\_id.integration\_id | [optional string](#string) | Return only deployments wired to this SYNQ integration (match against the deployment's connection\_mappings). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_triggerable\_by\_api.triggerable\_by\_api | [optional bool](#bool) | Filter by triggerable\_by\_api flag. |
| active\_only | [ bool](#bool) | When true (default), only active deployments are returned. |
| include\_paused | [ bool](#bool) | When false (default), paused deployments are excluded. |
### ListSuiteDeploymentsResponse
(synq.agent.recon.v1.ListSuiteDeploymentsResponse)
| Field | Type | Description |
| - | - | - |
| deployments | [repeated SuiteDeployment](#suitedeployment) | none |
| page\_info | [ PageInfo](#pageinfo) | none |
### PauseDeploymentRequest
(synq.agent.recon.v1.PauseDeploymentRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_until.until | [optional Timestamp](#timestamp) | Absent = pause indefinitely. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reason.reason | [optional string](#string) | none |
| actor | [ Actor](#actor) | Actor performing the pause. Required. |
### PauseDeploymentResponse
(synq.agent.recon.v1.PauseDeploymentResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### PromoteSuiteRequest
(synq.agent.recon.v1.PromoteSuiteRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | Suite to promote. For managed suites pass the suite name; for ad-hoc pass the UUID suite\_id. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_state\_at.state\_at | [optional Timestamp](#timestamp) | Specific version to promote. Absent = latest. |
| connection\_mappings | [repeated ConnectionMapping](#connectionmapping) | Mappings for suite connections that don't already resolve to a workspace integration by name. Connections whose name matches a workspace integration can be omitted; they are resolved automatically. Promotion fails if any connection in the suite cannot be resolved. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schedule.schedule | [optional SuiteSchedule](#suiteschedule) | Optional cron/iCal schedule. Absent on a re-promote preserves the existing deployment's schedule (cron or iCal) verbatim; absent on a fresh promote means no schedule. To remove a schedule from an existing deployment use UpdateDeploymentSchedule (clear), not a re-promote. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_triggerable\_by\_api.triggerable\_by\_api | [optional bool](#bool) | When true, a TriggerDeployment RPC may execute this deployment on demand. Optional so re-promotes can omit it: an absent value preserves the existing deployment's setting (a fresh promote defaults to false). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_bisection.run\_bisection | [optional bool](#bool) | Whether runs of this deployment drill into mismatched reconciliations after the quick check. Absent preserves the existing deployment's setting on a re-promote, or defaults to true on a fresh promote. Set false for quick-check-only deployments. Each reconciliation's own `bisection.enabled` still controls whether it is eligible to drill. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_timeout.execution\_timeout | [optional Duration](#duration) | Maximum wall-clock time for a single run of this deployment. Absent on a fresh promote uses the server default; absent on a re-promote preserves the existing deployment's setting. The server clamps the effective value to a supported range. Set a lower value to bound cost on expensive warehouses, or a higher value for large tables whose drill legitimately needs longer than the default. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deployment\_id.deployment\_id | [optional string](#string) | Optional client-supplied deployment id (UUID). Use this to make PromoteSuite idempotent across retries: if the original request wrote a row but the response was lost, the client re-issues with the same deployment\_id and the second call resolves to the same deployment. |
Server semantics: - absent : reuse the existing deployment for this suite\_id, or generate a fresh UUIDv7 - present, no row exists for (workspace, deployment\_id) : adopt as-is - present, row exists with same suite\_id : idempotent retry - present, row exists with different suite\_id : ALREADY\_EXISTS |
\| actor | [ Actor](#actor) | Actor performing the promotion. Required. |
\| deployment\_annotations | [repeated Annotation](#annotation) | Initial deployment-level annotation overlay. Merged with suite-level and case-level annotations from the frozen suite snapshot. Use UpdateDeploymentAnnotations to mutate later without re-promoting. |
\| reconciliation\_failure\_impacts | [map ReconciliationFailureImpactsEntry](#reconciliationfailureimpactsentry) | Per-case override controlling how the tables referenced by each reconciliation case propagate onto runs. Keyed by case `name` from the suite config. When a key is omitted on re-promote, the prior deployment's value is retained — operators only set keys they want to change, and the FE can offer an "apply to all" action by sending the same value under every case name. New cases (not present in any previous deployment of this suite\_id) default to RAISE\_ISSUE\_ON\_TABLES when unset.
Sending RECONCILIATION\_FAILURE\_IMPACT\_UNSPECIFIED for a known case is treated as "no opinion, keep current"; to actively reset to the default, either omit the case or send RAISE\_ISSUE\_ON\_TABLES. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Categories to declare for the reconciliation checks this promotion publishes. See SuiteDeployment.category / .governance\_category for what they mean and how they interact with annotations and categorisation rules.
On a re-promote these behave as a patch of the previous deployment's values, per field: - absent : keep whatever the previous deployment declared - present, empty string : clear the declaration, falling back to the reserved annotation or the workspace rules - present, non-empty : set or replace the declaration
So a re-promote that says nothing about categories never silently drops one. Use UpdateDeploymentCategories to change them without re-promoting. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | none |
\| case\_categories | [map CaseCategoriesEntry](#casecategoriesentry) | Initial per-case category overrides, keyed by case `name`. Keys omitted on a re-promote keep the previous deployment's override; see CaseCategories for how a single dimension is inherited or cleared. Overrides for cases the promoted snapshot doesn't contain are rejected with INVALID\_ARGUMENT, so a renamed or deleted case surfaces as an error rather than a silently dead override. |
### PromoteSuiteRequest.CaseCategoriesEntry
(synq.agent.recon.v1.PromoteSuiteRequest.CaseCategoriesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ CaseCategories](#casecategories) | none |
### PromoteSuiteRequest.ReconciliationFailureImpactsEntry
(synq.agent.recon.v1.PromoteSuiteRequest.ReconciliationFailureImpactsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ReconciliationFailureImpact](#reconciliationfailureimpact) | none |
### PromoteSuiteResponse
(synq.agent.recon.v1.PromoteSuiteResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### ResumeDeploymentRequest
(synq.agent.recon.v1.ResumeDeploymentRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| actor | [ Actor](#actor) | Actor performing the resume. Required. |
### ResumeDeploymentResponse
(synq.agent.recon.v1.ResumeDeploymentResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### SuiteDeployment
(synq.agent.recon.v1.SuiteDeployment)
SuiteDeployment is the latest state of a deployment (current row).
| Field | Type | Description |
| - | - | - |
| deployment\_id | [ string](#string) | Stable platform identity (UUIDv7). Preserved across re-promotes, pause/resume, and unpromote → re-promote cycles. Downstream AssetCommand / RunCommand reference this. |
| suite\_id | [ string](#string) | Suite this deployment came from. |
| suite\_name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_title.suite\_title | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_description.suite\_description | [optional string](#string) | none |
| active | [ bool](#bool) | False after UnpromoteSuite. Scheduler and triggers stop. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_paused\_until.paused\_until | [optional Timestamp](#timestamp) | When set and in the future, scheduled/triggered runs are suppressed. An indefinite pause uses a far-future sentinel timestamp. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_paused\_reason.paused\_reason | [optional string](#string) | none |
| suite | [ ReconciliationSuite](#reconciliationsuite) | Frozen snapshot of the suite config at promote time. |
| connection\_mappings | [repeated ConnectionMapping](#connectionmapping) | Mapping used at execution time to resolve DWH credentials. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schedule.schedule | [optional SuiteSchedule](#suiteschedule) | Schedule (if any). Absent = no cron; use triggerable\_by\_api. |
| triggerable\_by\_api | [ bool](#bool) | When true, this deployment can be run on demand via TriggerDeployment, in addition to any cron schedule. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_bisection.run\_bisection | [optional bool](#bool) | When true (the default), runs of this deployment drill into mismatched reconciliations via bisection after the quick check to locate the differing rows; when false, runs stop at the quick check. Each reconciliation's own `bisection.enabled` still controls whether it is eligible to drill, so this acts as the deployment-wide switch on top. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_timeout.execution\_timeout | [optional Duration](#duration) | Maximum wall-clock time for a single run of this deployment (the quick check plus any bisection drill). A run that exceeds it is cancelled and marked failed. Absent means the server default applies; the effective value is clamped to a supported range. Lower it to bound cost on expensive warehouses where a deep drill can run for a long time. |
| connection\_names | [repeated string](#string) | Denormalized summaries so listing callers don't need to parse the snapshot. |
| integration\_ids | [repeated string](#string) | none |
| reconciliation\_summaries | [repeated ReconciliationConfigSummary](#reconciliationconfigsummary) | none |
| source\_suite\_type | [ string](#string) | Where this deployment's snapshot came from. |
"managed" or "adhoc" |
\| source\_config\_state\_at | [ Timestamp](#timestamp) | none |
\| state\_at | [ Timestamp](#timestamp) | Most recent event timestamp. |
\| last\_action | [ DeploymentAction](#deploymentaction) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_actor.last\_actor | [optional Actor](#actor) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_change\_summary.last\_change\_summary | [optional string](#string) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_promoted\_at.promoted\_at | [optional Timestamp](#timestamp) | When this deployment reached production, and who put it there — taken from the most recent promotion in its history, not from whatever happened last.
Read these rather than state\_at / last\_actor whenever the question is "when did this go live and on whose authority". Pausing, rescheduling, editing annotations or changing categories all restamp state\_at / last\_actor and leave these two untouched, so a surface that renders the promotion from state\_at reports the wrong time and often the wrong person.
A re-promote is a promotion, so it restamps both. Unpromoting does not clear them: an inactive deployment still reports when it was last live. Both are absent only for a deployment whose history holds no promotion event. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_promoted\_by.promoted\_by | [optional Actor](#actor) | none |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_next\_run\_at.next\_run\_at | [optional Timestamp](#timestamp) | Next cron occurrence computed live from `schedule` at response time. Absent when there's no schedule, when the deployment is inactive/paused, or when the schedule expression no longer yields an upcoming occurrence (e.g. an iCal RRULE that has finished). Use this for "Next run: …" UI hints — it's the authoritative upcoming-tick source, independent of any pre-materialised execution\_jobs / run\_state rows. |
\| deployment\_annotations | [repeated Annotation](#annotation) | Deployment-level annotation overlay. Applied on top of the suite-level and case-level annotations baked into the frozen suite snapshot. The merged, deduplicated list lives on PromotedReconSuiteMetadata / PromotedReconCaseMetadata — this field exposes only the deployment's own overlay so callers can render and edit it independently. |
\| reconciliation\_failure\_impacts | [map ReconciliationFailureImpactsEntry](#reconciliationfailureimpactsentry) | Effective per-case ReconciliationFailureImpact map currently in force for this deployment. Keyed by case `name`. Cases without an entry behave as RAISE\_ISSUE\_ON\_TABLES. Drives whether the runner attaches each case's referenced tables to Run.target (issue-raising) or Run.extra\_references (visibility only) — see PromotedReconCaseMetadata.error\_tables / shown\_tables for the resolved table lists. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Technical category declared for every reconciliation check this deployment publishes — the "what kind of check is this, mechanically" dimension the catalog groups and filters checks by (for example "nullness", "uniqueness", "freshness"), which is what lets one category cover the same check written as a dbt test, a SQLMesh audit or a reconciliation.
Declared on the promotion rather than in the suite file, because a reconciliation only becomes a check once it is promoted: the suite file says what to reconcile, the promotion says how it behaves in production — the same reason `schedule` lives here and not in the file.
Absent means this deployment declares no category. The category is then taken from a reserved `quality.check_category` annotation on the suite, the case or the deployment overlay (see deployment\_annotations), and failing that from the workspace's own categorisation rules. Setting this field outranks both.
Free-form: the vocabulary is whatever the workspace already uses, not a fixed enum. Always read and write it together with governance\_category — the two dimensions resolve independently and a surface that exposes one must expose both. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | Governance category declared for every reconciliation check this deployment publishes — the "what is this check for" dimension, orthogonal to `category` (for example "completeness", "accuracy", "timeliness"). It is the dimension a data-governance function tracks coverage against.
Same declaration point, same precedence and same free-form vocabulary as `category`; resolved independently of it, so declaring one never suppresses the other's annotation. |
\| case\_categories | [map CaseCategoriesEntry](#casecategoriesentry) | Per-case overrides of the two fields above, keyed by case `name` from the suite config. Cases without an entry use the deployment-wide declaration.
Worth overriding when the cases of one suite are the same kind of check but serve different governance purposes — a case comparing row counts is about completeness while a case comparing values is about accuracy, and they can carry different governance categories while sharing the deployment's technical one. |
### SuiteDeployment.CaseCategoriesEntry
(synq.agent.recon.v1.SuiteDeployment.CaseCategoriesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ CaseCategories](#casecategories) | none |
### SuiteDeployment.ReconciliationFailureImpactsEntry
(synq.agent.recon.v1.SuiteDeployment.ReconciliationFailureImpactsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ReconciliationFailureImpact](#reconciliationfailureimpact) | none |
### SuiteDeploymentHistoryEntry
(synq.agent.recon.v1.SuiteDeploymentHistoryEntry)
SuiteDeploymentHistoryEntry is a single event in the deployment's history.
| Field | Type | Description |
| - | - | - |
| deployment\_id | [ string](#string) | none |
| suite\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | none |
| action | [ DeploymentAction](#deploymentaction) | none |
| active | [ bool](#bool) | State of the deployment at the moment of this event. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_paused\_until.paused\_until | [optional Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_paused\_reason.paused\_reason | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schedule.schedule | [optional SuiteSchedule](#suiteschedule) | none |
| triggerable\_by\_api | [ bool](#bool) | none |
| connection\_mappings | [repeated ConnectionMapping](#connectionmapping) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_bisection.run\_bisection | [optional bool](#bool) | The run\_bisection setting in force at the time of this event. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_timeout.execution\_timeout | [optional Duration](#duration) | The execution\_timeout in force at the time of this event. Absent means the server default applied. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite.suite | [optional ReconciliationSuite](#reconciliationsuite) | Only filled for PROMOTED / REPROMOTED events (snapshot changes). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source\_config\_state\_at.source\_config\_state\_at | [optional Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
| deployment\_annotations | [repeated Annotation](#annotation) | Deployment-level annotation overlay at this point in time. Filled on every history entry (annotation changes carry the full new list, other events carry the snapshot at the time of the event). |
| reconciliation\_failure\_impacts | [map ReconciliationFailureImpactsEntry](#reconciliationfailureimpactsentry) | Effective per-case ReconciliationFailureImpact map at this point in time. Snapshot semantics — every history entry carries the full map in force right after the event was recorded. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | The declared categories in force at this point in time. Snapshot semantics, like the fields above: every history entry carries the values in force right after the event was recorded, not just the CATEGORIES\_CHANGED ones. Absent means no category was declared at that time. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | none |
| case\_categories | [map CaseCategoriesEntry](#casecategoriesentry) | Per-case category overrides in force at this point in time. Snapshot semantics, like the map above. |
### SuiteDeploymentHistoryEntry.CaseCategoriesEntry
(synq.agent.recon.v1.SuiteDeploymentHistoryEntry.CaseCategoriesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ CaseCategories](#casecategories) | none |
### SuiteDeploymentHistoryEntry.ReconciliationFailureImpactsEntry
(synq.agent.recon.v1.SuiteDeploymentHistoryEntry.ReconciliationFailureImpactsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ReconciliationFailureImpact](#reconciliationfailureimpact) | none |
### TriggerDeploymentRequest
(synq.agent.recon.v1.TriggerDeploymentRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) lookup.suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) lookup.deployment\_id | [ string](#string) | none |
| actor | [ Actor](#actor) | Actor triggering the run. Required. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_bisection.run\_bisection | [optional bool](#bool) | Per-run override for the deployment's stored run\_bisection setting. Unset (the default) inherits the deployment's value, so an on-demand run behaves like a scheduled one. Set false for a cheap quick-check-only run with no bisection drill-down. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_timeout.execution\_timeout | [optional Duration](#duration) | Per-run override for the deployment's stored execution\_timeout. Unset (the default) inherits the deployment's value. Set it to bound this one run to a shorter or longer wall-clock budget; the server clamps the effective value to a supported range. |
### TriggerDeploymentResponse
(synq.agent.recon.v1.TriggerDeploymentResponse)
| Field | Type | Description |
| - | - | - |
| deployment\_id | [ string](#string) | Identity of the deployment that was triggered (canonical form even when the request used `suite_id`). |
| run\_id | [ string](#string) | Stable opaque identifier of the enqueued run. Pass this to RunStateService to track lifecycle. |
| scheduled\_at | [ Timestamp](#timestamp) | When the run is scheduled to start. For TriggerDeployment this is \~now(); the worker will pick it up shortly. |
### UnpromoteSuiteRequest
(synq.agent.recon.v1.UnpromoteSuiteRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reason.reason | [optional string](#string) | none |
| actor | [ Actor](#actor) | Actor performing the unpromote. Required. |
### UnpromoteSuiteResponse
(synq.agent.recon.v1.UnpromoteSuiteResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### UnresolvedConnection
(synq.agent.recon.v1.UnresolvedConnection)
One entry per connection name the server failed to resolve. Returned
inside ConnectionResolutionFailure as the structured error detail of a
FAILED\_PRECONDITION on adhoc submission or deployment promote/update.
| Field | Type | Description |
| - | - | - |
| connection\_name | [ string](#string) | Connection name as referenced by the suite config. |
| reason | [ UnresolvedConnectionReason](#unresolvedconnectionreason) | Why the server could not satisfy the mapping. |
| message | [ string](#string) | Optional human-readable elaboration ("integration X has reconciliation adhoc disabled by workspace admin", etc.). Already includes workspace / suite context — safe to surface verbatim. |
| candidate\_integration\_ids | [repeated string](#string) | Suggested integration ids the caller could pick from. Filtered by the capability the failing call needs (RECONCILIATION\_ADHOC for adhoc, RECONCILIATION\_DEPLOY for deployment) so the UI can render a picker directly without re-querying. |
### UpdateDeploymentAnnotationsRequest
(synq.agent.recon.v1.UpdateDeploymentAnnotationsRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| deployment\_annotations | [repeated Annotation](#annotation) | Full replacement for the deployment-level annotation overlay. Pass an empty list to clear all deployment-level annotations (suite/case annotations from the frozen snapshot still apply). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
| actor | [ Actor](#actor) | Actor performing the annotation update. Required. |
### UpdateDeploymentAnnotationsResponse
(synq.agent.recon.v1.UpdateDeploymentAnnotationsResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### UpdateDeploymentCaseImpactsRequest
(synq.agent.recon.v1.UpdateDeploymentCaseImpactsRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| reconciliation\_failure\_impacts | [map ReconciliationFailureImpactsEntry](#reconciliationfailureimpactsentry) | Partial patch of the per-case impact map. See UpdateDeploymentCaseImpacts RPC docs for merge / clear semantics. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
| actor | [ Actor](#actor) | Actor performing the update. Required. |
### UpdateDeploymentCaseImpactsRequest.ReconciliationFailureImpactsEntry
(synq.agent.recon.v1.UpdateDeploymentCaseImpactsRequest.ReconciliationFailureImpactsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ReconciliationFailureImpact](#reconciliationfailureimpact) | none |
### UpdateDeploymentCaseImpactsResponse
(synq.agent.recon.v1.UpdateDeploymentCaseImpactsResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### UpdateDeploymentCategoriesRequest
(synq.agent.recon.v1.UpdateDeploymentCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Patch of the declared categories. Field presence is meaningful and the two fields are independent: - absent : leave the current value unchanged - present, empty string : clear the declaration - present, non-empty : set or replace the declaration |
A request that sets nothing at all is accepted and changes nothing. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | none |
\| case\_categories | [map CaseCategoriesEntry](#casecategoriesentry) | Patch of the per-case overrides, keyed by case `name`. Keys present in the request replace that case's override; keys absent are left untouched, so this never resets overrides the caller didn't mention. Send an entry with both fields absent to drop a case's override and go back to inheriting the deployment's declaration. Unknown case names are rejected with INVALID\_ARGUMENT. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
\| actor | [ Actor](#actor) | Actor performing the category update. Required. |
### UpdateDeploymentCategoriesRequest.CaseCategoriesEntry
(synq.agent.recon.v1.UpdateDeploymentCategoriesRequest.CaseCategoriesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ CaseCategories](#casecategories) | none |
### UpdateDeploymentCategoriesResponse
(synq.agent.recon.v1.UpdateDeploymentCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### UpdateDeploymentMappingsRequest
(synq.agent.recon.v1.UpdateDeploymentMappingsRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| connection\_mappings | [repeated ConnectionMapping](#connectionmapping) | Same shape as PromoteSuiteRequest.connection\_mappings — entries are optional and apply only to connections that don't resolve by name. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
| actor | [ Actor](#actor) | Actor performing the mapping update. Required. |
### UpdateDeploymentMappingsResponse
(synq.agent.recon.v1.UpdateDeploymentMappingsResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### UpdateDeploymentScheduleRequest
(synq.agent.recon.v1.UpdateDeploymentScheduleRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schedule.schedule | [optional SuiteSchedule](#suiteschedule) | Presence-based: - absent = preserve current schedule - present, empty = clear the schedule (an empty SuiteSchedule has no cron, which the deployment row stores as no schedule) - present, with cron = replace the schedule |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_triggerable\_by\_api.triggerable\_by\_api | [optional bool](#bool) | Absent = preserve current setting. Otherwise update triggerable\_by\_api. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_summary.change\_summary | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_run\_bisection.run\_bisection | [optional bool](#bool) | Absent = preserve current setting. Otherwise update the deployment's run\_bisection flag. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_timeout.execution\_timeout | [optional Duration](#duration) | Absent = preserve current setting. Otherwise update the deployment's execution\_timeout (maximum wall-clock budget for a single run). The server clamps the effective value to a supported range at run time. |
| actor | [ Actor](#actor) | Actor performing the schedule update. Required. |
### UpdateDeploymentScheduleResponse
(synq.agent.recon.v1.UpdateDeploymentScheduleResponse)
| Field | Type | Description |
| - | - | - |
| deployment | [ SuiteDeployment](#suitedeployment) | none |
### ConnectionEnvironmentOverride
(synq.agent.recon.v1.ConnectionEnvironmentOverride)
ConnectionEnvironmentOverride applies to all datasets using a given connection.
Provides a default table path override for all datasets on that connection.
| Field | Type | Description |
| - | - | - |
| table\_path | [ TablePathOverride](#tablepathoverride) | Default table path override for all datasets on this connection. Can be further overridden by dataset-level overrides. |
### DatasetEnvironmentOverride
(synq.agent.recon.v1.DatasetEnvironmentOverride)
DatasetEnvironmentOverride applies to a specific dataset in a specific reconciliation.
Takes precedence over connection-level overrides.
| Field | Type | Description |
| - | - | - |
| table\_path | [ TablePathOverride](#tablepathoverride) | Table path override for this specific dataset. |
### ReconEnvironment
(synq.agent.recon.v1.ReconEnvironment)
ReconEnvironment defines overrides applied to a suite before execution.
Environments allow the same suite definition to run against different
database locations (e.g., dev vs production schemas) without modifying
the suite itself.
Used both in synq-recon CLI (loaded from .env.yaml files) and in
kernel-recon cloud (managed as workspace-level configurations).
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Environment name (e.g., "dev-lukasz", "staging", "production"). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Human-readable description of this environment. |
| connections | [map ConnectionsEntry](#connectionsentry) | Connection-level overrides, keyed by connection name. Applied to all datasets using the specified connection. |
| datasets | [map DatasetsEntry](#datasetsentry) | Dataset-level overrides, keyed by "reconciliation\_name.source" or "reconciliation\_name.target". Takes precedence over connection-level overrides for the matching dataset. |
| variables | [map VariablesEntry](#variablesentry) | Variable overrides merged on top of suite-level variables. Takes precedence over suite variables but is overridden by CLI --var flags. |
### ReconEnvironment.ConnectionsEntry
(synq.agent.recon.v1.ReconEnvironment.ConnectionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ConnectionEnvironmentOverride](#connectionenvironmentoverride) | none |
### ReconEnvironment.DatasetsEntry
(synq.agent.recon.v1.ReconEnvironment.DatasetsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ DatasetEnvironmentOverride](#datasetenvironmentoverride) | none |
### ReconEnvironment.VariablesEntry
(synq.agent.recon.v1.ReconEnvironment.VariablesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### TablePathOverride
(synq.agent.recon.v1.TablePathOverride)
TablePathOverride remaps components of a fully-qualified table name.
Only set fields are overridden — unset fields keep the suite's original value.
Applied to TableReference fields in datasets when an environment is active.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Replace the database/catalog component. e.g., "PROD\_DB" → "DEV\_LUKASZ\_DB" |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schema.schema | [optional string](#string) | Replace the schema/dataset component. e.g., "analytics" → "dev\_lukasz\_analytics" |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_table\_prefix.table\_prefix | [optional string](#string) | Prefix to prepend to the table name. e.g., "dev\_" → "dev\_orders" instead of "orders" |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_table\_suffix.table\_suffix | [optional string](#string) | Suffix to append to the table name. e.g., "\_staging" → "orders\_staging" instead of "orders" |
### BatchReconciliationRunStateRequest
(synq.agent.recon.v1.BatchReconciliationRunStateRequest)
| Field | Type | Description |
| - | - | - |
| suite\_ids | [repeated string](#string) | none |
| include\_adhoc\_children | [ bool](#bool) | When true, the response also includes adhoc test runs spawned from the queried suites — i.e. rows whose parent\_suite\_id matches. Use this for the "development" view where adhoc activity is part of the iteration loop. Production / deployment-centric views leave it false to see only deployment-driven runs. |
### BatchReconciliationRunStateResponse
(synq.agent.recon.v1.BatchReconciliationRunStateResponse)
| Field | Type | Description |
| - | - | - |
| states | [map StatesEntry](#statesentry) | Map keyed by suite\_id; suites with no recorded runs are omitted. |
### BatchReconciliationRunStateResponse.StatesEntry
(synq.agent.recon.v1.BatchReconciliationRunStateResponse.StatesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ SuiteRunStates](#suiterunstates) | none |
### CancelReconciliationRunRequest
(synq.agent.recon.v1.CancelReconciliationRunRequest)
| Field | Type | Description |
| - | - | - |
| suite\_id | [ string](#string) | The suite the run belongs to. Required because run rows are keyed by (workspace, suite\_id, run\_id) — clients pass the suite\_id they already know from the run state listing alongside the run\_id. |
| run\_id | [ string](#string) | Run identifier. UUIDv7 returned by BatchReconciliationRunState / ListReconciliationRunState. |
### CancelReconciliationRunResponse
(synq.agent.recon.v1.CancelReconciliationRunResponse)
| Field | Type | Description |
| - | - | - |
| state | [ ReconciliationRunState](#reconciliationrunstate) | The current state of the run after the cancel attempt. Status will be CANCELLED if the run was SCHEDULED/IN\_PROGRESS at call time, or unchanged if it had already reached a terminal status. |
### ListReconciliationRunStateRequest
(synq.agent.recon.v1.ListReconciliationRunStateRequest)
| Field | Type | Description |
| - | - | - |
| suite\_ids | [repeated string](#string) | Optional filter: limit to these suite\_ids. Combined with deployment\_ids via OR — a row matches if either list contains it (both empty = no suite/deployment filter). |
| statuses | [repeated ReconciliationRunStatus](#reconciliationrunstatus) | Optional filter: only return runs in these statuses. Empty = no status filter. |
| deployment\_ids | [repeated string](#string) | Optional filter: limit to runs belonging to these deployment\_ids. Use this to scope the listing to a single promoted deployment cycle — suite\_id alone may overlap multiple deployment\_ids when a suite has been promoted, unpromoted, and re-promoted. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scheduled\_after.scheduled\_after | [optional Timestamp](#timestamp) | Only return runs scheduled at or after this timestamp (inclusive). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scheduled\_before.scheduled\_before | [optional Timestamp](#timestamp) | Only return runs scheduled before this timestamp (exclusive). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination parameters. The cursor encodes (scheduled\_at, run\_id) and is opaque to clients — pass page\_info.next\_cursor verbatim. |
| triggers | [repeated ReconciliationRunTrigger](#reconciliationruntrigger) | Optional filter: only return runs created via these triggers. Empty = no trigger filter. Use this to separate API-triggered runs from cron-scheduled occurrences from ad-hoc submissions in the listing. |
| actor\_identities | [repeated string](#string) | Optional filter: limit to runs initiated by these actor identities. Identities use the canonical actor-identity string format defined alongside the public Actor type (e.g. `email:foo@example.com`, `slack:U12345`, `client_id:`). Empty list = no actor filter. Useful for "show only my ad-hoc runs" views — clients construct their own identity from the authenticated user (typically `email:`) and pass it here. |
### ListReconciliationRunStateResponse
(synq.agent.recon.v1.ListReconciliationRunStateResponse)
| Field | Type | Description |
| - | - | - |
| states | [repeated ReconciliationRunState](#reconciliationrunstate) | Sorted newest-first by scheduled\_at, then by run\_id desc as a stable tiebreaker. |
| page\_info | [ PageInfo](#pageinfo) | Pagination info for fetching the next page. |
### SuiteRunStates
(synq.agent.recon.v1.SuiteRunStates)
SuiteRunStates groups the most relevant run rows for a single suite.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_latest\_concluded.latest\_concluded | [optional ReconciliationRunState](#reconciliationrunstate) | Most recent run that has reached a terminal status (SUCCEEDED, FAILED, CANCELLED). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ongoing.ongoing | [optional ReconciliationRunState](#reconciliationrunstate) | Most recent run whose target tick has arrived (scheduled\_at ≤ now) and that is still SCHEDULED or IN\_PROGRESS. Pre-scheduled future rows are not surfaced here — see `next_run_at` on SuiteDeployment for the upcoming cron occurrence. |
### ColumnMapping
(synq.agent.recon.v1.ColumnMapping)
ColumnMapping maps a source column name to a target column name.
Used when source and target use different naming conventions for the same data
(e.g., snake\_case vs SCREAMING\_SNAKE\_CASE, or completely different names).
Only columns with different names need explicit mapping — columns with
identical names (or case-insensitive matches when case\_insensitive is true)
are matched automatically.
| Field | Type | Description |
| - | - | - |
| source | [ string](#string) | Column name in the source dataset. |
| target | [ string](#string) | Corresponding column name in the target dataset. |
### Dataset
(synq.agent.recon.v1.Dataset)
Dataset defines a source or target dataset for reconciliation.
A dataset specifies which connection to use and how to select data —
either via a raw SQL query or by referencing a table with optional column filtering.
| Field | Type | Description |
| - | - | - |
| connection | [ string](#string) | Name of the connection to use. Must reference a connection configured in the runner's connection map. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data\_source.query | [ string](#string) | Raw SQL query returning the dataset. Should be a SELECT or WITH (CTE) statement. Supports template variable interpolation via \{\{variable\_name}} syntax. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data\_source.table | [ TableReference](#tablereference) | Table reference with optional column selection. Automatically generates a SELECT query from the table metadata. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_as\_of.as\_of | [optional string](#string) | Time-travel timestamp for snapshot queries (e.g., "2026-02-01 00:00:00"). When set, wraps the query with database-specific time-travel syntax: - Snowflake: AT(TIMESTAMP => '\') - BigQuery: FOR SYSTEM\_TIME AS OF TIMESTAMP '\' - Databricks: TIMESTAMP AS OF '\' Has no effect on databases that don't support time-travel. |
| columns | [repeated string](#string) | Columns of the query's result to compare. When set, only these columns are read from the query; the rest of its SELECT list is ignored. Only with query — a table takes its columns inside table. Mutually exclusive with exclude\_columns. |
| exclude\_columns | [repeated string](#string) | Columns of the query's result to leave out of the comparison. Every other column is read (resolved at runtime from the query's result shape). Only with query — a table takes its exclusions inside table. Mutually exclusive with columns. |
### TableReference
(synq.agent.recon.v1.TableReference)
TableReference specifies a table and optional column filtering.
Use this instead of a raw query when you want to compare all (or most) columns
of a table without writing SQL.
`name` is the table or view name (e.g., "orders", "fact\_sales").
`database` and `schema` are optional namespace qualifiers.
The actual SQL FQN is generated at runtime using the appropriate dialect quoting.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Table or view name (e.g., "orders", "fact\_sales"). Required — this is the actual object name. |
| columns | [repeated string](#string) | Explicit list of columns to include in the comparison. When set, only these columns are selected from the table. Mutually exclusive with exclude\_columns. |
| exclude\_columns | [repeated string](#string) | Columns to exclude from the comparison. All columns except these are selected (resolved at runtime via table metadata). Useful for skipping volatile columns (e.g., updated\_at, etl\_batch\_id). Mutually exclusive with columns. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Database/catalog name (e.g., "PROD\_RAW", "my\_project"). Maps to: Snowflake database, BigQuery project, Databricks catalog. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schema.schema | [optional string](#string) | Schema/dataset name (e.g., "public", "analytics"). Maps to: Snowflake schema, BigQuery dataset, Databricks schema. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_where.where | [optional string](#string) | Row filter — appended as WHERE clause to the generated SELECT. Example: "created\_at ≥ '2024-01-01' AND status = 'active'" |
### AggregateDrillChild
(synq.agent.recon.v1.AggregateDrillChild)
AggregateDrillChild represents a single divergent group within a drill-down level.
| Field | Type | Description |
| - | - | - |
| key | [ Struct](#struct) | Group key values (column name -> value). |
| type | [ AggregateDivergenceType](#aggregatedivergencetype) | Type of divergence. |
| source\_values | [map SourceValuesEntry](#sourcevaluesentry) | Source measure values (measure key -> value). |
| target\_values | [map TargetValuesEntry](#targetvaluesentry) | Target measure values (measure key -> value). |
| differences | [ MeasureDifferences](#measuredifferences) | All difference variants computed for this group. |
| threshold\_evaluation | [ ThresholdEvaluation](#thresholdevaluation) | Threshold evaluation results for this group. |
| source\_query | [ string](#string) | Investigation query for source data. |
| target\_query | [ string](#string) | Investigation query for target data. |
| drill\_down | [ AggregateDrillNode](#aggregatedrillnode) | Further drill-down into this divergent group. |
### AggregateDrillChild.SourceValuesEntry
(synq.agent.recon.v1.AggregateDrillChild.SourceValuesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### AggregateDrillChild.TargetValuesEntry
(synq.agent.recon.v1.AggregateDrillChild.TargetValuesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### AggregateDrillNode
(synq.agent.recon.v1.AggregateDrillNode)
AggregateDrillNode represents a node in the hierarchical aggregate drill-down tree.
| Field | Type | Description |
| - | - | - |
| level | [ int32](#int32) | Drill-down level (0-based). |
| group\_column | [ string](#string) | Group column used at this level. |
| total\_groups | [ int32](#int32) | Total groups at this level. |
| matched\_groups | [ int32](#int32) | Number of matched groups at this level. |
| children | [repeated AggregateDrillChild](#aggregatedrillchild) | Divergent children (mismatched or missing groups). |
### AggregateStageResult
(synq.agent.recon.v1.AggregateStageResult)
AggregateStageResult is the result for an aggregate\_check stage.
| Field | Type | Description |
| - | - | - |
| total\_groups | [ int32](#int32) | Top-level (first group column) total group count. With a multi-level drill-down this is the number of distinct first-level groups, NOT the number of leaf groups. Invariant: total\_groups == matched\_groups + divergent\_groups. |
| matched\_groups | [ int32](#int32) | Top-level groups that matched — directly, or because their whole subtree resolved. Same level as total\_groups. |
| mismatch\_groups | [ int32](#int32) | Leaf-level groups with value differences (deepest drill level). NOT comparable to total\_groups: one divergent top-level group can contain many divergent leaves. For a same-level "X of Y diverged" ratio use divergent\_groups / total\_groups; use these leaf counts for detail. |
| missing\_source | [ int32](#int32) | Leaf-level groups present in target but missing in source. |
| missing\_target | [ int32](#int32) | Leaf-level groups present in source but missing in target. |
| match | [ bool](#bool) | Overall match result. |
| early\_termination\_reason | [ string](#string) | Reason if aggregate check terminated early. |
| drill\_down | [ AggregateDrillNode](#aggregatedrillnode) | Hierarchical drill-down tree for aggregate comparison. |
| divergent\_groups | [ int32](#int32) | Top-level groups that diverged (counterpart of matched\_groups, same level as total\_groups). This is the same-level numerator for a "divergent\_groups of total\_groups diverged" summary — unlike the leaf-level mismatch\_groups / missing\_\* counts above, which can exceed total\_groups in a multi-level drill. |
### AuditLog
(synq.agent.recon.v1.AuditLog)
AuditLog is the root structure for a reconciliation run audit.
It captures all operations performed during a synq-recon reconciliation run.
| Field | Type | Description |
| - | - | - |
| version | [ string](#string) | Schema version. Must be "1". |
| invocation\_id | [ string](#string) | Unique identifier for this invocation/run. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_parent\_invocation\_id.parent\_invocation\_id | [optional string](#string) | Invocation ID of the parent run this continues from. Set when this run resumes or drills deeper into a previous run's results. |
| suite\_name | [ string](#string) | Suite machine identifier (promoted from suite.name). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_title.suite\_title | [optional string](#string) | Human-readable title (promoted from suite.title). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_suite\_description.suite\_description | [optional string](#string) | Suite description (promoted from suite.description). |
| started\_at | [ Timestamp](#timestamp) | When the run started. |
| completed\_at | [ Timestamp](#timestamp) | When the run completed. |
| duration | [ Duration](#duration) | Total run duration. |
| status | [ AuditOutcome](#auditoutcome) | Overall execution status derived from individual reconciliation outcomes. |
| summary | [ AuditSummary](#auditsummary) | Aggregated counts derived from individual reconciliation results. |
| reconciliations | [repeated ReconciliationAudit](#reconciliationaudit) | Audit records for each reconciliation executed. |
| meta | [ AuditMeta](#auditmeta) | Build and environment metadata for the synq-recon binary. |
| config\_file | [ string](#string) | Path to the configuration file used. |
| suite | [ ReconciliationSuite](#reconciliationsuite) | Full suite configuration snapshot as executed. Contains all reconciliation definitions, variables, thresholds, etc. |
| resolved\_variables | [map ResolvedVariablesEntry](#resolvedvariablesentry) | Resolved template variable values used for query interpolation. Contains the actual values after template evaluation (e.g. "\{\{today - 30d}}" resolved to "2026-01-27"). |
| warnings | [repeated string](#string) | Suite-level warnings detected during config validation. |
| total\_stats | [ QueryStats](#querystats) | Aggregated query execution statistics across all queries in this run. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_environment.environment | [optional string](#string) | Name of the environment used for this run. Empty for direct/unmanaged runs where no environment was applied. |
| resolved\_table\_references | [map ResolvedTableReferencesEntry](#resolvedtablereferencesentry) | Effective table references after environment overrides were applied. Keyed by "reconciliation\_name.source" or "reconciliation\_name.target". Only populated when an environment was active and overrides were applied. The suite field (14) still contains the original pre-override config. |
### AuditLog.ResolvedTableReferencesEntry
(synq.agent.recon.v1.AuditLog.ResolvedTableReferencesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### AuditLog.ResolvedVariablesEntry
(synq.agent.recon.v1.AuditLog.ResolvedVariablesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### AuditMeta
(synq.agent.recon.v1.AuditMeta)
AuditMeta holds build and environment metadata for the synq-recon binary.
| Field | Type | Description |
| - | - | - |
| version | [ string](#string) | Module version or tag (e.g. v1.2.3). |
| vcs\_commit | [ string](#string) | VCS revision hash. |
| vcs\_time | [ string](#string) | VCS commit timestamp (RFC3339). |
| vcs\_dirty | [ bool](#bool) | True if the working tree had uncommitted changes. |
| go\_version | [ string](#string) | Go toolchain version used to build the binary. |
### AuditSummary
(synq.agent.recon.v1.AuditSummary)
AuditSummary holds aggregated counts derived from individual reconciliation results.
| Field | Type | Description |
| - | - | - |
| overall\_match | [ bool](#bool) | True when every reconciliation matched (exactly or within threshold). False when any reconciliation failed or had an exceeding mismatch. |
| total | [ int32](#int32) | Total number of reconciliations executed. |
| matched | [ int32](#int32) | Number of reconciliations that completed and matched exactly. |
| matched\_within\_threshold | [ int32](#int32) | Number of reconciliations that had differences within configured thresholds. These are warning-level outcomes — data differs but within acceptable tolerance. |
| mismatched | [ int32](#int32) | Number of reconciliations that completed but found data differences exceeding thresholds (or no thresholds configured). |
| failed | [ int32](#int32) | Number of reconciliations that encountered an infrastructure error. |
| errors | [repeated string](#string) | Error messages collected from failed reconciliations. |
### BisectionStageResult
(synq.agent.recon.v1.BisectionStageResult)
BisectionStageResult is the result for a bisection\_drill stage.
| Field | Type | Description |
| - | - | - |
| total\_segments | [ int32](#int32) | Total number of segments processed. |
| mismatch\_count | [ int32](#int32) | Number of mismatched leaf segments. |
| error\_count | [ int32](#int32) | Number of segments that encountered errors. |
| max\_depth | [ int32](#int32) | Maximum depth reached in the segment tree. |
| early\_termination\_reason | [ string](#string) | Reason if bisection terminated early. |
| statistics | [ BisectionStatistics](#bisectionstatistics) | Detailed tree statistics. |
| mismatch\_leaves | [repeated MismatchLeafDetail](#mismatchleafdetail) | Details for each mismatched leaf segment. |
| investigation\_queries | [repeated InvestigationQuery](#investigationquery) | Merged investigation queries for adjacent mismatch regions. |
### BisectionStatistics
(synq.agent.recon.v1.BisectionStatistics)
BisectionStatistics holds statistics about the bisection segment tree.
| Field | Type | Description |
| - | - | - |
| total\_nodes | [ int32](#int32) | none |
| matched\_nodes | [ int32](#int32) | none |
| mismatched\_nodes | [ int32](#int32) | none |
| leaf\_nodes | [ int32](#int32) | none |
| intermediate\_nodes | [ int32](#int32) | none |
| matched\_leaves | [ int32](#int32) | none |
| mismatched\_leaves | [ int32](#int32) | none |
| depth\_distribution | [map DepthDistributionEntry](#depthdistributionentry) | Number of segments at each depth level. |
| mismatch\_by\_depth | [map MismatchByDepthEntry](#mismatchbydepthentry) | Number of mismatched segments at each depth level. |
| per\_depth | [map PerDepthEntry](#perdepthentry) | Detailed per-depth statistics. |
| total\_source\_rows | [ int64](#int64) | none |
| total\_target\_rows | [ int64](#int64) | none |
| mismatch\_source\_rows | [ int64](#int64) | none |
| mismatch\_target\_rows | [ int64](#int64) | none |
### BisectionStatistics.DepthDistributionEntry
(synq.agent.recon.v1.BisectionStatistics.DepthDistributionEntry)
| Field | Type | Description |
| - | - | - |
| key | [ int32](#int32) | none |
| value | [ int32](#int32) | none |
### BisectionStatistics.MismatchByDepthEntry
(synq.agent.recon.v1.BisectionStatistics.MismatchByDepthEntry)
| Field | Type | Description |
| - | - | - |
| key | [ int32](#int32) | none |
| value | [ int32](#int32) | none |
### BisectionStatistics.PerDepthEntry
(synq.agent.recon.v1.BisectionStatistics.PerDepthEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ DepthStats](#depthstats) | none |
### CutoffResult
(synq.agent.recon.v1.CutoffResult)
CutoffResult captures the runtime resolution of a cutoff filter.
Stored in the audit log to provide full transparency into how the cutoff
was derived and applied.
| Field | Type | Description |
| - | - | - |
| source\_watermark | [ string](#string) | Watermark value derived from the source dataset. Empty when cutoff was configured for target-only derivation. |
| target\_watermark | [ string](#string) | Watermark value derived from the target dataset. Empty when cutoff was configured for source-only derivation. |
| cutoff\_value | [ string](#string) | Final cutoff value after combining source/target watermarks and applying truncation and offset transforms. |
| source\_where | [ string](#string) | WHERE clause applied to the source query. |
| target\_where | [ string](#string) | WHERE clause applied to the target query. |
### DatasetInfo
(synq.agent.recon.v1.DatasetInfo)
DatasetInfo captures runtime information about a source or target dataset.
| Field | Type | Description |
| - | - | - |
| connection | [ string](#string) | Connection name used. |
| dialect | [ string](#string) | Database dialect detected at runtime (e.g. "postgres", "snowflake"). |
| query | [ string](#string) | Resolved SQL query actually executed (after variable interpolation). |
### DepthStats
(synq.agent.recon.v1.DepthStats)
DepthStats holds per-depth-level statistics for the bisection tree.
| Field | Type | Description |
| - | - | - |
| total\_rows | [ int64](#int64) | none |
| mismatched\_rows | [ int64](#int64) | none |
| segments | [ int32](#int32) | none |
| mismatched\_segments | [ int32](#int32) | none |
### DiffQueries
(synq.agent.recon.v1.DiffQueries)
DiffQueries holds investigation queries for a mismatch leaf.
| Field | Type | Description |
| - | - | - |
| source\_rows | [ string](#string) | SQL query to retrieve source rows in the mismatch segment. |
| target\_rows | [ string](#string) | SQL query to retrieve target rows in the mismatch segment. |
### InvestigationQuery
(synq.agent.recon.v1.InvestigationQuery)
InvestigationQuery represents a merged diff query covering one or more
adjacent mismatch leaves.
| Field | Type | Description |
| - | - | - |
| min\_key | [ Value](#value) | Minimum key (inclusive). |
| max\_key | [ Value](#value) | Maximum key (exclusive). |
| time\_bucket | [ string](#string) | Time bucket label (for time-based segmentation). |
| leaves\_covered | [ int32](#int32) | Number of mismatch leaves covered by this query. |
| source\_query | [ string](#string) | SQL query to retrieve source rows. |
| target\_query | [ string](#string) | SQL query to retrieve target rows. |
### MeasureDifferences
(synq.agent.recon.v1.MeasureDifferences)
MeasureDifferences holds all difference variants for measure comparisons.
| Field | Type | Description | | | | | | |
| - | - | - | - | - | - | - | - | - |
| relative | [map RelativeEntry](#relativeentry) | Relative difference per measure (target - source). Positive means target is larger. | | | | | | |
| absolute | [map AbsoluteEntry](#absoluteentry) | Absolute difference per measure: | target - source | . | | | | |
| pct\_vs\_source | [map PctVsSourceEntry](#pctvssourceentry) | Percentage difference relative to source: | diff | / | source | . | | |
| pct\_vs\_target | [map PctVsTargetEntry](#pctvstargetentry) | Percentage difference relative to target: | diff | / | target | . | | |
| pct\_symmetric | [map PctSymmetricEntry](#pctsymmetricentry) | Symmetric percentage difference: 2\* | diff | / ( | source | + | target | ). |
### MeasureDifferences.AbsoluteEntry
(synq.agent.recon.v1.MeasureDifferences.AbsoluteEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### MeasureDifferences.PctSymmetricEntry
(synq.agent.recon.v1.MeasureDifferences.PctSymmetricEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### MeasureDifferences.PctVsSourceEntry
(synq.agent.recon.v1.MeasureDifferences.PctVsSourceEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### MeasureDifferences.PctVsTargetEntry
(synq.agent.recon.v1.MeasureDifferences.PctVsTargetEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### MeasureDifferences.RelativeEntry
(synq.agent.recon.v1.MeasureDifferences.RelativeEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ double](#double) | none |
### MeasureVerdict
(synq.agent.recon.v1.MeasureVerdict)
MeasureVerdict captures the threshold evaluation result for a single measure.
| Field | Type | Description |
| - | - | - |
| exceeded | [ bool](#bool) | True when the measure difference exceeds all configured thresholds. |
| reason | [ MeasureVerdictReason](#measureverdictreason) | Explains why the measure was considered within or outside thresholds. |
| effective\_absolute | [ double](#double) | Absolute threshold applied to this measure (may differ via per\_measure config). |
| effective\_percentage | [ double](#double) | Percentage threshold applied to this measure. |
| effective\_percentage\_basis | [ string](#string) | Percentage basis applied to this measure, as a PercentageBasis value name. |
### MismatchLeafDetail
(synq.agent.recon.v1.MismatchLeafDetail)
MismatchLeafDetail holds detailed information about a single mismatch leaf.
| Field | Type | Description |
| - | - | - |
| segment | [ SegmentInfo](#segmentinfo) | Segment boundaries. |
| source\_count | [ int64](#int64) | none |
| target\_count | [ int64](#int64) | none |
| source\_checksum | [ int64](#int64) | none |
| target\_checksum | [ int64](#int64) | none |
| count\_difference | [ int64](#int64) | none |
| mismatch\_types | [repeated SegmentMismatchType](#segmentmismatchtype) | Types of mismatch detected. |
| drill\_stop\_reason | [ DrillStopReason](#drillstopreason) | Why bisection stopped at this leaf. |
| diff\_queries | [ DiffQueries](#diffqueries) | Investigation queries to inspect this mismatch. |
| row\_mismatches | [repeated RowMismatchDetail](#rowmismatchdetail) | Per-row mismatch details (when reporting level allows). |
| row\_mismatch\_count | [ int32](#int32) | Total number of row mismatches (may exceed row\_mismatches length due to sample\_limit). |
### Normalization
(synq.agent.recon.v1.Normalization)
Normalization records the column expressions used after cross-DB type alignment.
| Field | Type | Description |
| - | - | - |
| source\_columns | [repeated string](#string) | Source column expressions after normalization. |
| target\_columns | [repeated string](#string) | Target column expressions after normalization. |
| numeric\_columns | [repeated NumericComparison](#numericcomparison) | The precision each decimal and floating-point column was compared at, in column order. A MATCH on such a column is a match at this precision. |
### NumericComparison
(synq.agent.recon.v1.NumericComparison)
NumericComparison records the precision one numeric column was compared at.
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Column name as the source side reports it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) precision.decimal\_places | [ int32](#int32) | Both sides were rounded to this many decimal places. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) precision.significant\_digits | [ int32](#int32) | Both sides were compared at this many significant digits, as floating-point values are by default. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) precision.exact | [ bool](#bool) | Both sides were compared at the full precision the column holds. |
| configured | [ bool](#bool) | True when the precision was set by the suite's comparison settings, false when it was derived from the column types on both sides. |
### QueryRecord
(synq.agent.recon.v1.QueryRecord)
QueryRecord captures a single SQL query execution.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Unique query identifier within this run. |
| role | [ QueryRole](#queryrole) | Whether this query ran against source or target. |
| connection | [ string](#string) | Connection name used. |
| dialect | [ string](#string) | Database dialect. |
| sql | [ string](#string) | SQL query text. |
| started\_at | [ Timestamp](#timestamp) | When the query started. |
| duration | [ Duration](#duration) | Query execution duration. |
| rows\_returned | [ int32](#int32) | Number of result rows returned. |
| error | [ string](#string) | Error message if the query failed. |
| retry\_attempt | [ int32](#int32) | Retry attempt number (0 for first attempt). |
| stats | [ QueryStats](#querystats) | Execution statistics collected from the database driver. |
### QueryStats
(synq.agent.recon.v1.QueryStats)
QueryStats holds execution statistics collected from the database driver.
Fields are optional — absent means the metric is not available for the driver.
| Field | Type | Description |
| - | - | - |
| query\_id | [ string](#string) | Database-assigned query identifier for auditing. Available for BigQuery (job ID), Snowflake, ClickHouse. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_rows\_read.rows\_read | [optional int64](#int64) | Number of rows read/scanned by the query engine. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bytes\_read.bytes\_read | [optional int64](#int64) | Number of bytes read/scanned by the query engine. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_rows\_produced.rows\_produced | [optional int64](#int64) | Number of result rows returned to the caller. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cache\_hit.cache\_hit | [optional bool](#bool) | Whether the query result was served from cache. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bytes\_billed.bytes\_billed | [optional int64](#int64) | Number of bytes billed (BigQuery). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_slot\_millis.slot\_millis | [optional int64](#int64) | Slot time consumed (BigQuery). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_blocks.blocks | [optional int64](#int64) | Number of data blocks read (ClickHouse). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_completed\_splits.completed\_splits | [optional int64](#int64) | Number of completed splits (Trino). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cpu\_time\_millis.cpu\_time\_millis | [optional int64](#int64) | CPU time consumed (Trino). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_wall\_time\_millis.wall\_time\_millis | [optional int64](#int64) | Wall time reported by the engine (Trino). |
### QuickCheckStageResult
(synq.agent.recon.v1.QuickCheckStageResult)
QuickCheckStageResult is the result for a quick\_check stage.
| Field | Type | Description |
| - | - | - |
| source\_count | [ int64](#int64) | none |
| target\_count | [ int64](#int64) | none |
| source\_checksum | [ int64](#int64) | none |
| target\_checksum | [ int64](#int64) | none |
| match | [ bool](#bool) | none |
| count\_difference | [ int64](#int64) | none |
| error | [ string](#string) | none |
| threshold\_evaluation | [ ThresholdEvaluation](#thresholdevaluation) | Threshold evaluation of the row count difference, keyed by the measure "COUNT(\*)". Set only in ROW\_COUNT mode with row\_count.thresholds configured. A difference that is not exceeded is within tolerance. |
### ReconciliationAudit
(synq.agent.recon.v1.ReconciliationAudit)
ReconciliationAudit captures the configuration and runtime execution data
for a single reconciliation.
| Field | Type | Description |
| - | - | - |
| reconciliation | [ Reconciliation](#reconciliation) | The reconciliation configuration as executed. |
| source | [ DatasetInfo](#datasetinfo) | Runtime dataset information (resolved queries, detected dialects). |
| target | [ DatasetInfo](#datasetinfo) | none |
| original\_source\_query | [ string](#string) | Source query before variable interpolation (differs from suite query when variables are used). |
| original\_target\_query | [ string](#string) | Target query before variable interpolation. |
| columns | [repeated string](#string) | Column names discovered at runtime via QueryShape. |
| normalization | [ Normalization](#normalization) | Column normalization expressions applied after cross-DB type alignment. |
| stages | [repeated StageAudit](#stageaudit) | Execution stages (quick\_check, bisection\_drill, aggregate\_check). |
| warnings | [repeated string](#string) | Per-reconciliation warnings. |
| status | [ ReconciliationOutcome](#reconciliationoutcome) | Outcome status of this reconciliation. Distinguishes infrastructure errors from data mismatches and threshold-based outcomes. |
| overall\_match | [ bool](#bool) | Whether source and target matched overall. True when data matched exactly or differences were within configured thresholds. |
| error | [ string](#string) | Error message if the reconciliation failed due to an infrastructure error. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cutoff\_result.cutoff\_result | [optional CutoffResult](#cutoffresult) | Cutoff resolution details (when cutoff was configured). Captures the derived watermark values, final cutoff, and WHERE clauses so the run can be fully understood and reproduced. |
### RowMismatchAudit
(synq.agent.recon.v1.RowMismatchAudit)
RowMismatchAudit captures a single row-level mismatch in the segment tree.
| Field | Type | Description |
| - | - | - |
| pk | [ Value](#value) | Primary key value. |
| type | [ RowMismatchType](#rowmismatchtype) | Type of row mismatch. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source\_row\_hash.source\_row\_hash | [optional int64](#int64) | Source row hash (when available). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_target\_row\_hash.target\_row\_hash | [optional int64](#int64) | Target row hash (when available). |
| source\_values | [map SourceValuesEntry](#sourcevaluesentry) | Source column values (detailed reporting only). |
| target\_values | [map TargetValuesEntry](#targetvaluesentry) | Target column values (detailed reporting only). |
### RowMismatchAudit.SourceValuesEntry
(synq.agent.recon.v1.RowMismatchAudit.SourceValuesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### RowMismatchAudit.TargetValuesEntry
(synq.agent.recon.v1.RowMismatchAudit.TargetValuesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### RowMismatchDetail
(synq.agent.recon.v1.RowMismatchDetail)
RowMismatchDetail holds per-row mismatch detail.
| Field | Type | Description |
| - | - | - |
| pk | [ Value](#value) | Primary key value. |
| type | [ RowMismatchType](#rowmismatchtype) | Type of row mismatch. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source\_row\_hash.source\_row\_hash | [optional int64](#int64) | Source row hash (when available). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_target\_row\_hash.target\_row\_hash | [optional int64](#int64) | Target row hash (when available). |
### SegmentAuditNode
(synq.agent.recon.v1.SegmentAuditNode)
SegmentAuditNode represents a node in the bisection segment tree.
| Field | Type | Description |
| - | - | - |
| segment | [ SegmentInfo](#segmentinfo) | Segment boundaries. |
| source\_count | [ int64](#int64) | none |
| target\_count | [ int64](#int64) | none |
| source\_checksum | [ int64](#int64) | none |
| target\_checksum | [ int64](#int64) | none |
| match | [ bool](#bool) | Whether source and target matched for this segment. |
| is\_leaf | [ bool](#bool) | Whether this is a leaf node (no further splitting). |
| drill\_stop\_reason | [ DrillStopReason](#drillstopreason) | Why bisection stopped at this node. |
| mismatch\_rows\_in\_subtree | [ int64](#int64) | Sum of mismatched rows in this subtree. |
| total\_rows\_in\_subtree | [ int64](#int64) | Sum of all rows in this subtree. |
| query\_ids | [repeated string](#string) | References to QueryRecord.id values for queries that populated this node. |
| children | [repeated SegmentAuditNode](#segmentauditnode) | Child segments (empty for leaf nodes). |
| row\_mismatches | [repeated RowMismatchAudit](#rowmismatchaudit) | Row-level mismatches (only for leaf nodes with detailed reporting). |
### SegmentInfo
(synq.agent.recon.v1.SegmentInfo)
SegmentInfo identifies a key range segment.
| Field | Type | Description |
| - | - | - |
| min\_key | [ Value](#value) | Minimum key (inclusive). Unset means unbounded. |
| max\_key | [ Value](#value) | Maximum key (exclusive). Unset means unbounded. |
| depth | [ int32](#int32) | Depth in the bisection tree. |
| index | [ int32](#int32) | Index within the depth level. |
| time\_bucket | [ string](#string) | Time bucket label (for time-based segmentation). |
### StageAudit
(synq.agent.recon.v1.StageAudit)
StageAudit captures one reconciliation stage execution.
| Field | Type | Description |
| - | - | - |
| stage | [ StageType](#stagetype) | Stage type. |
| started\_at | [ Timestamp](#timestamp) | When the stage started. |
| completed\_at | [ Timestamp](#timestamp) | When the stage completed. |
| duration | [ Duration](#duration) | Stage duration. |
| queries | [repeated QueryRecord](#queryrecord) | SQL queries executed during this stage. |
| segment\_tree | [ SegmentAuditNode](#segmentauditnode) | Bisection segment tree (only for bisection\_drill stage). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) result.quick\_check\_result | [ QuickCheckStageResult](#quickcheckstageresult) | Result for quick\_check stage. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) result.bisection\_result | [ BisectionStageResult](#bisectionstageresult) | Result for bisection\_drill stage. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) result.aggregate\_result | [ AggregateStageResult](#aggregatestageresult) | Result for aggregate\_check stage. |
### ThresholdEvaluation
(synq.agent.recon.v1.ThresholdEvaluation)
ThresholdEvaluation captures the effective thresholds applied and per-measure verdicts.
| Field | Type | Description |
| - | - | - |
| absolute\_threshold | [ double](#double) | Configured maximum absolute difference, or 0 if not set. |
| percentage\_threshold | [ double](#double) | Configured maximum percentage difference, or 0 if not set. |
| percentage\_basis | [ string](#string) | Which percentage formula was used, as a PercentageBasis value name. |
| measures | [map MeasuresEntry](#measuresentry) | Per-measure threshold evaluation results. |
### ThresholdEvaluation.MeasuresEntry
(synq.agent.recon.v1.ThresholdEvaluation.MeasuresEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ MeasureVerdict](#measureverdict) | none |
### AggregateConfig
(synq.agent.recon.v1.AggregateConfig)
AggregateConfig defines aggregate comparison settings.
Used when mode is AGGREGATE to compare grouped measures between source and target.
| Field | Type | Description |
| - | - | - |
| measures | [repeated Measure](#measure) | Measures to compare between source and target. Each measure defines a column and one or more aggregate functions to apply. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_thresholds.thresholds | [optional ThresholdConfig](#thresholdconfig) | Tolerance thresholds for aggregate comparisons. When not set, exact match is required for all measures. |
| group\_columns | [repeated string](#string) | Columns defining the drill-down hierarchy for aggregate comparison. Falls back to the key columns if not set. With no key columns either, the whole dataset is compared as a single group, with no GROUP BY. |
Single column: flat GROUP BY comparison. group\_columns: \["region"] → GROUP BY region
Multiple columns: cumulative GROUP BY drill-down, pruning matched groups at each level to focus on divergent branches. group\_columns: \["region", "city", "store"] → Level 0: GROUP BY region → Level 1: GROUP BY region, city (only for mismatched regions) → Level 2: GROUP BY region, city, store (only for mismatched cities) |
### BisectionConfig
(synq.agent.recon.v1.BisectionConfig)
BisectionConfig controls the bisection drill-down algorithm that recursively
splits the key range to locate individual mismatched rows.
Only used in ROW\_CHECKSUM mode.
| Field | Type | Description |
| - | - | - |
| enabled | [ bool](#bool) | Whether bisection drill-down is enabled. When false, only the quick-check (count + checksum) runs — mismatches are detected but not localized to specific rows. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_factor.factor | [optional int32](#int32) | Branching factor: how many segments each level is split into. Higher values find mismatches faster (fewer levels) but issue more queries per level. Default: 32. Typical range: 4–64. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_threshold.threshold | [optional int32](#int32) | Row count threshold: stop bisecting when a segment has fewer rows than this. Lower values find more precise mismatch locations but issue more queries. Default: 16384. Set to 1 to drill down to individual rows. |
| strategy | [ SegmentationStrategy](#segmentationstrategy) | Segmentation strategy for splitting key ranges. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_time\_column.time\_column | [optional string](#string) | Column for time-based partitioning. Required when strategy is TIME. Must be a timestamp, date, or datetime column in the dataset. |
| time\_granularity | [ TimeGranularity](#timegranularity) | Granularity for time-based segmentation. Default: DAY. Controls the width of each time bucket when strategy is TIME. |
### ColumnComparison
(synq.agent.recon.v1.ColumnComparison)
ColumnComparison holds the comparison settings of one column.
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Column name as the source side reports it, matched case-insensitively. With a column\_mapping, this is the source column of the pair. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_numeric\_scale.numeric\_scale | [optional NumericScale](#numericscale) | How many decimal places this column is compared at, or exact. Overrides the reconciliation's and the suite's numeric\_scale. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_timestamp\_precision.timestamp\_precision | [optional TimestampPrecision](#timestampprecision) | How much of a second this timestamp column is compared to. Overrides the reconciliation's and the suite's timestamp\_precision. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source\_time\_zone.source\_time\_zone | [optional string](#string) | The time zone the source side holds this column's wall clock in. Overrides the reconciliation's and the suite's source\_time\_zone. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_target\_time\_zone.target\_time\_zone | [optional string](#string) | The time zone the target side holds this column's wall clock in. Overrides the reconciliation's and the suite's target\_time\_zone. |
### ComparisonConfig
(synq.agent.recon.v1.ComparisonConfig)
ComparisonConfig controls how column values are compared in ROW\_CHECKSUM
mode. It has no effect in ROW\_COUNT and AGGREGATE modes.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_numeric\_scale.numeric\_scale | [optional NumericScale](#numericscale) | How many decimal places decimal and floating-point columns are compared at, or exact. When not set, a decimal column is compared at the scale both sides share, and a floating-point column at the digits both sides hold. Integer, text, timestamp and other columns are not affected. |
| columns | [repeated ColumnComparison](#columncomparison) | Settings for individual columns, overriding the settings above. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_timestamp\_precision.timestamp\_precision | [optional TimestampPrecision](#timestampprecision) | How much of a second timestamp columns are compared to, timestamp key columns included. When not set, a timestamp is compared at the precision both sides hold, the coarser of the two. Date, numeric, text and other columns are not affected. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source\_time\_zone.source\_time\_zone | [optional string](#string) | The time zone the source side's timestamps without a time zone hold their wall clock in, as an IANA name such as Europe/Warsaw. When not set, they are read as UTC. A timestamp with a time zone already says which instant it is and is not affected. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_target\_time\_zone.target\_time\_zone | [optional string](#string) | The time zone the target side's timestamps without a time zone hold their wall clock in, as source\_time\_zone does for the source side. |
### ConnectionQueries
(synq.agent.recon.v1.ConnectionQueries)
ConnectionQueries associates SQL queries with a specific connection.
Used for setup and teardown blocks at both suite and reconciliation level.
| Field | Type | Description |
| - | - | - |
| connection | [ string](#string) | Connection name to execute these queries on. Must reference a connection configured in the runner's connection map. |
| queries | [repeated string](#string) | SQL queries to execute in order on this connection. |
### ErrorHandlingConfig
(synq.agent.recon.v1.ErrorHandlingConfig)
ErrorHandlingConfig controls retry and timeout behavior for database queries.
Applies to all queries within a reconciliation (setup, comparison, teardown).
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_query\_timeout.query\_timeout | [optional Duration](#duration) | Per-query timeout. When a query exceeds this duration, it is cancelled and fails with a timeout error. It is not retried, since a query that took this long will usually take it again. Covers setup and teardown statements too. To bound the whole reconciliation rather than each query, use its timeout or the suite's execution.reconciliation\_timeout. Default: no per-query limit. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_max\_retries.max\_retries | [optional int32](#int32) | Maximum retry attempts for transient query failures. Setup and teardown statements are never retried, since they need not be safe to run twice. The total number of attempts is max\_retries + 1 (initial + retries). Default: 2. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_retry\_initial\_delay.retry\_initial\_delay | [optional Duration](#duration) | Initial backoff delay before the first retry. Subsequent retries multiply this by retry\_backoff\_factor. Default: 1s. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_retry\_backoff\_factor.retry\_backoff\_factor | [optional double](#double) | Backoff multiplier applied after each retry. Delay for attempt N = retry\_initial\_delay \* retry\_backoff\_factor^(N-1). Must be ≥ 1.0 to ensure delays don't decrease. Default: 2.0. |
### ExecutionConfig
(synq.agent.recon.v1.ExecutionConfig)
ExecutionConfig holds the suite's execution settings. Every field is
optional, and an absent field leaves the runner's default in place.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_timeout.timeout | [optional Duration](#duration) | Maximum wall-clock time for a single run of the suite (the quick check plus any bisection drill). A run that exceeds it is cancelled and the reconciliations still running are reported as failed. Server-side runs clamp the value to a supported range. |
Precedence, most specific first: the per-run override, the deployment's execution\_timeout, this field, the runner default. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_concurrency.concurrency | [optional int32](#int32) | Maximum number of reconciliations run in parallel. 1 (the default) runs them one after another. Each parallel reconciliation issues its own queries, so this multiplies the load on the source and target warehouses. Server-side runs clamp the value to a supported maximum. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reconciliation\_timeout.reconciliation\_timeout | [optional Duration](#duration) | Maximum wall-clock time for a single reconciliation of the suite: its setup, quick check and any drill. A reconciliation that exceeds it is stopped and recorded as failed with a timeout error, and the run moves on to the next one, so one slow query cannot use up the whole run's timeout. It never extends the run: whichever of the two runs out first applies.
A reconciliation's own timeout takes precedence. Unset means only the run's timeout bounds a reconciliation. |
### Measure
(synq.agent.recon.v1.Measure)
Measure defines a column and aggregate function(s) to compare
between source and target datasets.
A single measure with multiple functions expands into multiple comparisons
(e.g., column="amount" functions=\[SUM, AVG] produces "SUM(amount)" and "AVG(amount)").
| Field | Type | Description |
| - | - | - |
| column | [ string](#string) | Column name to aggregate. Must exist in both source and target datasets (after column mapping is applied). |
| functions | [repeated AggregateFunction](#aggregatefunction) | Aggregate functions to apply to this column. Multiple functions produce multiple measure comparisons from one column. |
### NumericScale
(synq.agent.recon.v1.NumericScale)
NumericScale is the precision a numeric column is compared at.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) value.places | [ int32](#int32) | Round both sides to this many decimal places before comparing, whatever their types. A value that differs only beyond the last place matches. Use it to tolerate a known rounding by a loader, or to compare a floating-point column with a decimal one. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) value.exact | [ bool](#bool) | Compare at the full precision the column holds. A decimal is compared at its declared scale, which must be known and the same on both sides, otherwise the reconciliation fails rather than rounding either side. A floating-point column is compared digit for digit, which needs both sides on the same kind of database: across two different ones the run fails with a message instead. Must be true when set. |
### Reconciliation
(synq.agent.recon.v1.Reconciliation)
Reconciliation defines a single reconciliation scenario comparing data
between a source and target dataset. Each reconciliation runs independently
and produces its own pass/mismatch/fail result.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Unique machine identifier within the suite. Alphanumerics, hyphens, and underscores. May be a UUID. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | Human-readable title. Displayed in reports. Defaults to name when not set. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Optional longer description explaining what this reconciliation validates. |
| source | [ Dataset](#dataset) | Source dataset to compare from — the "expected" or "authoritative" side. |
| target | [ Dataset](#dataset) | Target dataset to compare against — the "actual" or "replicated" side. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_key\_column.key\_column | [optional string](#string) | Deprecated: use key\_columns instead. Single primary key column used for ordering and segmentation during bisection. Retained for backward compatibility — existing single-key configs and stored audit logs keep working. When key\_columns is set, this field is ignored. Readers should resolve the effective key via key\_columns first, falling back to this. |
| key\_columns | [repeated string](#string) | Ordered list of key columns used for ordering and segmentation during bisection. Supports composite (multi-column) keys; the bisection orders and range-filters on the column tuple so the engine can prune via a matching primary key / index. A single-element list is equivalent to setting key\_column. Required for ROW\_CHECKSUM and ROW\_COUNT modes (this or the deprecated key\_column). Optional in AGGREGATE mode, where it only stands in for group\_columns when those are unset. |
| mode | [ ReconciliationMode](#reconciliationmode) | Comparison mode controlling how source and target are compared. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_hash\_algorithm.hash\_algorithm | [optional HashAlgorithm](#hashalgorithm) | Hash algorithm for row checksums in ROW\_CHECKSUM mode. When unset, auto-negotiates the best common algorithm between the source and target database dialects. |
| column\_mapping | [repeated ColumnMapping](#columnmapping) | Explicit column name mapping between source and target. Only columns with different names need mapping — identical names (or case-insensitive matches) are matched automatically. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_case\_insensitive.case\_insensitive | [optional bool](#bool) | When true, automatically matches columns differing only in letter case (e.g., user\_id matches USER\_ID). Defaults to true when not set. Set to false to require exact case matching. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bisection.bisection | [optional BisectionConfig](#bisectionconfig) | Bisection drill-down configuration for ROW\_CHECKSUM mode. Controls how the key range is recursively split to locate mismatches. When not set, uses sensible defaults (factor=32, threshold=16384). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reporting.reporting | [optional ReportingConfig](#reportingconfig) | Reporting output configuration controlling how much detail is included in the audit log for mismatched rows. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_aggregate.aggregate | [optional AggregateConfig](#aggregateconfig) | Aggregate comparison settings. Required when mode is AGGREGATE. Defines which measures to compare and tolerance thresholds. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_error\_handling.error\_handling | [optional ErrorHandlingConfig](#errorhandlingconfig) | Error handling and retry configuration for database queries. Controls timeouts and retry behavior for transient failures. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_window\.window | [optional WindowConfig](#windowconfig) | Time window for incremental comparison. When set, automatically provides a \{\{window\_start}} template variable for filtering queries to a recent time range. |
| setup | [repeated ConnectionQueries](#connectionqueries) | Setup queries run before this specific reconciliation. Executes after suite-level setup but before data comparison. |
| teardown | [repeated ConnectionQueries](#connectionqueries) | Teardown queries run after this specific reconciliation completes. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_teardown\_on\_failure.teardown\_on\_failure | [optional bool](#bool) | Whether teardown runs on failure for this reconciliation. When not set, inherits from suite-level teardown\_on\_failure. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_ignore\_setup\_errors.ignore\_setup\_errors | [optional bool](#bool) | When true, logs setup errors as warnings and continues. When not set, inherits from suite-level ignore\_setup\_errors. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cutoff.cutoff | [optional CutoffConfig](#cutoffconfig) | Dynamic cutoff filter for sync reconciliation. Derives a watermark from actual data to exclude rows not yet synced. Applied as WHERE filter to both source and target queries at runtime. |
| annotations | [repeated Annotation](#annotation) | Case-level annotations. Merged with suite-level annotations and deployment-level annotations at promote time, then exposed on the case asset (PromotedReconCaseMetadata). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_timeout.timeout | [optional Duration](#duration) | Maximum wall-clock time for this reconciliation: its setup, quick check and any drill. Overrides the suite's execution.reconciliation\_timeout. A reconciliation that exceeds it is recorded as failed with a timeout error and the run moves on to the next one. It never extends the run's own timeout. For a bound on each individual query, use error\_handling.query\_timeout. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_row\_count.row\_count | [optional RowCountConfig](#rowcountconfig) | Row count comparison settings. Only used when mode is ROW\_COUNT. Sets the tolerance a difference in row counts may stay within. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_comparison.comparison | [optional ComparisonConfig](#comparisonconfig) | How column values are compared in ROW\_CHECKSUM mode. Overrides the suite's comparison settings. When not set, the suite's apply, and without those every column is compared at the precision its types on both sides allow. |
### ReconciliationSuite
(synq.agent.recon.v1.ReconciliationSuite)
ReconciliationSuite is the root configuration for a reconciliation suite.
It defines template variables and one or more reconciliation scenarios
that compare data between source and target databases.
Database connections are configured separately and referenced by name.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Short machine identifier for the suite. Unique machine identifier for the suite, used as the stable key for tracking runs over time. Alphanumerics, hyphens, and underscores. May be a UUID. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | Human-readable title for the suite. Displayed in dashboards and reports. Defaults to name when not set. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Optional longer description of the suite's purpose. |
| reconciliations | [repeated Reconciliation](#reconciliation) | Reconciliation scenarios to execute. Each reconciliation compares data between a source and target dataset. |
| setup | [repeated ConnectionQueries](#connectionqueries) | Setup queries run once before all reconciliations begin. Use for creating temp tables, loading fixtures, or preparing the environment. Queries execute in order; all connections run sequentially. |
| teardown | [repeated ConnectionQueries](#connectionqueries) | Teardown queries run once after all reconciliations complete. Use for cleaning up temp tables or restoring state. |
| teardown\_on\_failure | [ bool](#bool) | Whether teardown runs even when reconciliations fail with infrastructure errors. When false (default), teardown is skipped on failure to preserve state for debugging. |
| ignore\_setup\_errors | [ bool](#bool) | When true, logs setup query errors as warnings and continues execution instead of aborting the run. Useful when setup creates IF NOT EXISTS objects. |
| variables | [map VariablesEntry](#variablesentry) | Template variables for query interpolation via \{\{variable\_name}} syntax. Values can be literal strings or template expressions: - Literal: "2026-01-01" - Time expression: "\{\{today - 30d}}", "\{\{now - 2h}}" - Built-in: "\{\{now}}", "\{\{today}}", "\{\{window\_start}}" Variables are resolved once at the start of the run and frozen. |
| strict\_time\_references | [ bool](#bool) | When true, time reference detection (e.g. NOW(), CURRENT\_DATE, GETDATE() in queries) returns an error instead of a warning. Prevents non-deterministic queries that produce different results on each run. |
| annotations | [repeated Annotation](#annotation) | Suite-level annotations. Each annotation is a name with zero or more string values. Applied to the deployed suite asset and inherited by every reconciliation case under the suite. Merged with per-case annotations and deployment-level annotations at promote time — the final, deduplicated list is exposed on PromotedReconSuiteMetadata / PromotedReconCaseMetadata and surfaced to the rest of the SYNQ platform via EntityAnnotations. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution.execution | [optional ExecutionConfig](#executionconfig) | How runs of this suite execute: the wall-clock budget of a run and how many reconciliations run at the same time. Read by every run of the suite, local or server-side; a run-level override (a CLI flag, the execution\_timeout on a trigger or on a deployment) takes precedence. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_comparison.comparison | [optional ComparisonConfig](#comparisonconfig) | Default comparison settings for every reconciliation in the suite. A reconciliation's own comparison settings take precedence, field by field. |
### ReconciliationSuite.VariablesEntry
(synq.agent.recon.v1.ReconciliationSuite.VariablesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### ReportingConfig
(synq.agent.recon.v1.ReportingConfig)
ReportingConfig controls the level of detail included in reconciliation output
for mismatched rows. Higher detail levels reveal more data but may have
privacy implications.
| Field | Type | Description |
| - | - | - |
| level | [ MismatchDetailLevel](#mismatchdetaillevel) | Output detail level for mismatched rows. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sample\_limit.sample\_limit | [optional int32](#int32) | Maximum number of sample rows to include in output per mismatch segment. Limits the size of the audit log when there are many mismatches. When not set, all mismatched rows within the reporting level are included. |
| consent\_acknowledged | [ bool](#bool) | Explicit acknowledgment that detailed row data may be included in the audit log. Required when level is DETAILED to prevent accidental exposure of sensitive data. The caller must set this to true to confirm they understand the privacy implications. |
### RowCountConfig
(synq.agent.recon.v1.RowCountConfig)
RowCountConfig defines row count comparison settings.
Used when mode is ROW\_COUNT.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_thresholds.thresholds | [optional ThresholdConfig](#thresholdconfig) | Tolerance for the difference between the source and target row counts. A difference within it reports MISMATCHED\_WITHIN\_THRESHOLD instead of MISMATCHED, and no drill runs for it. Same semantics as aggregate thresholds, applied to COUNT(\*): a difference is accepted when it is within absolute or within percentage, computed against percentage\_basis. When not set, the counts must be equal. |
### WindowConfig
(synq.agent.recon.v1.WindowConfig)
WindowConfig defines a time window for incremental comparison.
When configured, automatically provides a \{\{window\_start}} template variable
that resolves to (current\_time - lookback). Use this in query WHERE clauses
to limit comparison to recent data.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_column.column | [optional string](#string) | Column being windowed. Informational metadata for the audit trail — the actual filtering is done via the \{\{window\_start}} variable in your query WHERE clause. |
| lookback | [ Duration](#duration) | How far back to look from the current time. Defines the window as \[now - lookback, now]. Examples: "336h" (14 days), "2h", "720h" (30 days). |
| strategy | [ WindowStrategy](#windowstrategy) | Windowing strategy controlling how boundaries are computed. |
### IdentityRule
(synq.entities.identities.v2.IdentityRule)
IdentityRule declares how entities are recognized as the same logical entity.
A rule is evaluated against the current set of entities, so it automatically
applies to entities that appear after the rule is created. The `kind` field
selects the matching strategy and must be set to a supported rule type.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.prefix\_rewrite\_identity\_rule | [ PrefixRewriteIdentityRule](#prefixrewriteidentityrule) | none |
### PrefixRewriteIdentityRule
(synq.entities.identities.v2.PrefixRewriteIdentityRule)
PrefixRewriteIdentityRule treats two identifier prefixes as the same instance:
any entity whose identifier begins with source\_prefix is considered identical
to the entity with the same identifier under target\_prefix. The relationship
is bidirectional.
| Field | Type | Description |
| - | - | - |
| source\_prefix | [ string](#string) | Identifier prefix to match (for example, an instance or account prefix). |
| target\_prefix | [ string](#string) | Identifier prefix that matched entities are unified with. |
### BatchChangeRequest
(synq.entities.changes.v1.BatchChangeRequest)
BatchChangeRequest contains the parameters needed to retrieve multiple changes by their IDs.
| Field | Type | Description |
| - | - | - |
| change\_ids | [repeated string](#string) | List of change IDs to retrieve. Each ID uniquely identifies a specific change in the system. |
### BatchChangeResponse
(synq.entities.changes.v1.BatchChangeResponse)
BatchChangeResponse contains the requested changes mapped by their IDs.
| Field | Type | Description |
| - | - | - |
| changes | [map ChangesEntry](#changesentry) | Map of change ID to EntityChange. Only includes changes that were found and accessible. Missing changes are omitted from the response. |
### BatchChangeResponse.ChangesEntry
(synq.entities.changes.v1.BatchChangeResponse.ChangesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ EntityChange](#entitychange) | none |
### ListRequest
(synq.entities.changes.v1.ListRequest)
ListRequest contains the parameters needed to retrieve changes for a specific entity.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get changes for. This can be a path identifier that uniquely identifies the entity in the system. |
| ids | [repeated Identifier](#identifier) | Identifiers of entities for which to get changes. Change is returned when it affects id. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_before.change\_before | [optional Timestamp](#timestamp) | Return changes older than or on `before`. Defaults to `now()`. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_change\_after.change\_after | [optional Timestamp](#timestamp) | Return changes newer than until. Defaults to before - 7days, |
| limit\_to\_change\_types | [repeated EntityChangeType](#entitychangetype) | Return only changes of specific types. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination parameters for limiting and cursoring through results. |
### ListResponse
(synq.entities.changes.v1.ListResponse)
ListResponse contains the list of changes made to the requested entity.
| Field | Type | Description |
| - | - | - |
| changes | [repeated EntityChange](#entitychange) | List of recent changes to the state of entity. Changes are returned from newest to oldest. Each change includes: - The type of change (git commit, schema change, SQL definition change) - When the change occurred - Details specific to the type of change - Metadata about who made the change |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_page\_info.page\_info | [optional PageInfo](#pageinfo) | Pagination information for navigating through results. |
### BatchSchemaMismatchesRequest
(synq.entities.schemas.v1.BatchSchemaMismatchesRequest)
BatchGetSchemaMismatchesRequest is the request message for the BatchGetSchemaMismatches method.
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | Identifiers of the entities to get schema mismatches for. |
### BatchSchemaMismatchesResponse
(synq.entities.schemas.v1.BatchSchemaMismatchesResponse)
BatchGetSchemaMismatchesResponse is the response message for the BatchGetSchemaMismatches method.
| Field | Type | Description |
| - | - | - |
| schema\_mismatches | [repeated SchemaMismatch](#schemamismatch) | Schema mismatches found for the requested entities. |
### SchemaMismatch
(synq.entities.schemas.v1.SchemaMismatch)
SchemaMismatch represents a schema drift between a definer entity (e.g., dbt model)
and an origin entity (e.g., BigQuery table) where columns are defined but missing in origin.
| Field | Type | Description |
| - | - | - |
| definer\_id | [ Identifier](#identifier) | Identifier of the entity that defines the columns (e.g., dbt model). |
| origin\_id | [ Identifier](#identifier) | Identifier of the entity where the columns are expected but missing (e.g., BigQuery table). |
| missing\_origin\_column\_names | [repeated string](#string) | Names of the columns that are defined but missing in the origin. |
| missing\_origin\_column\_human\_names | [repeated string](#string) | Human-readable names of the columns that are defined but missing in the origin. |
### GetSchemaRequest
(synq.entities.schemas.v1.GetSchemaRequest)
GetEntityRequest is the request message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get. |
### GetSchemaResponse
(synq.entities.schemas.v1.GetSchemaResponse)
GetEntityResponse is the response message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schema.schema | [optional Schema](#schema) | Schema of the entity, might be empty |
### GetTableConstraintsRequest
(synq.entities.constraints.v1.GetTableConstraintsRequest)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get constraints for. |
### GetTableConstraintsResponse
(synq.entities.constraints.v1.GetTableConstraintsResponse)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity whose constraints are returned. May differ from the requested identifier if identity resolution mapped the request to an underlying table (e.g. dbt model → warehouse table). |
| synq\_path | [ string](#string) | Coalesce Quality path of the resolved entity. |
| constraints | [repeated TableConstraint](#tableconstraint) | Constraints and indexes defined on the table. |
| database\_instance | [ string](#string) | Database coordinates of the table whose constraints are returned. |
| database\_name | [ string](#string) | none |
| schema\_name | [ string](#string) | none |
| object\_name | [ string](#string) | none |
### CheckRelationship
(synq.entities.custom.v1.CheckRelationship)
| Field | Type | Description |
| - | - | - |
| check | [ Identifier](#identifier) | none |
| checked | [ Identifier](#identifier) | none |
| checked\_columns | [repeated string](#string) | none |
### CheckRelationshipWriteResult
(synq.entities.custom.v1.CheckRelationshipWriteResult)
CheckRelationshipWriteResult reports what one requested check relationship
did to the stored graph.
| Field | Type | Description |
| - | - | - |
| check\_relationship | [ CheckRelationship](#checkrelationship) | The check relationship this result answers for, as sent, with `entity_id` filled in on both endpoints. |
The pair of entities the endpoints resolve to is what the relationship is held under, so compare these two ids rather than the identifiers you sent to tell whether two of them are the same relationship. `checked_columns` is part of the payload, not of that pair: restating the same pair with a different column list replaces the stored one. |
\| outcome | [ RelationshipWriteOutcome](#relationshipwriteoutcome) | What the write did to that relationship. |
### DeleteCheckRelationshipsRequest
(synq.entities.custom.v1.DeleteCheckRelationshipsRequest)
| Field | Type | Description |
| - | - | - |
| check\_relationships | [repeated CheckRelationship](#checkrelationship) | none |
### DeleteCheckRelationshipsResponse
(synq.entities.custom.v1.DeleteCheckRelationshipsResponse)
| Field | Type | Description |
| - | - | - |
| results | [repeated CheckRelationshipWriteResult](#checkrelationshipwriteresult) | One result per check relationship in the request, in request order. |
### ListCheckRelationshipsRequest
(synq.entities.custom.v1.ListCheckRelationshipsRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every check relationship in one response — there is no server-side default page size. Set page\_size to bound the response; pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListCheckRelationshipsResponse
(synq.entities.custom.v1.ListCheckRelationshipsResponse)
| Field | Type | Description |
| - | - | - |
| check\_relationships | [repeated CheckRelationship](#checkrelationship) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. Repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty to walk the whole workspace. |
### UpsertCheckRelationshipsRequest
(synq.entities.custom.v1.UpsertCheckRelationshipsRequest)
| Field | Type | Description |
| - | - | - |
| check\_relationships | [repeated CheckRelationship](#checkrelationship) | none |
### UpsertCheckRelationshipsResponse
(synq.entities.custom.v1.UpsertCheckRelationshipsResponse)
| Field | Type | Description |
| - | - | - |
| results | [repeated CheckRelationshipWriteResult](#checkrelationshipwriteresult) | One result per check relationship in the request, in request order. |
### DeleteEntityRequest
(synq.entities.custom.v1.DeleteEntityRequest)
DeleteEntityRequest is the request message for the DeleteEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to delete. |
### DeleteEntityResponse
(synq.entities.custom.v1.DeleteEntityResponse)
DeleteEntityResponse is the response message for the DeleteEntity method.
### ListEntitiesRequest
(synq.entities.custom.v1.ListEntitiesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every entity in one response — there is no server-side default page size. Set page\_size to bound the response; pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListEntitiesResponse
(synq.entities.custom.v1.ListEntitiesResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated Entity](#entity) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. Repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty to walk the whole workspace. |
### UpsertEntityRequest
(synq.entities.custom.v1.UpsertEntityRequest)
UpsertEntityRequest is the request message for the UpsertEntity method.
| Field | Type | Description |
| - | - | - |
| entity | [ Entity](#entity) | The entity to create or update. |
### UpsertEntityResponse
(synq.entities.custom.v1.UpsertEntityResponse)
UpsertEntityResponse is the response message for the UpsertEntity method.
### DeleteRelationshipsRequest
(synq.entities.custom.v1.DeleteRelationshipsRequest)
| Field | Type | Description |
| - | - | - |
| relationships | [repeated Relationship](#relationship) | none |
### DeleteRelationshipsResponse
(synq.entities.custom.v1.DeleteRelationshipsResponse)
| Field | Type | Description |
| - | - | - |
| results | [repeated RelationshipWriteResult](#relationshipwriteresult) | One result per relationship in the request, in request order. |
### ListRelationshipsRequest
(synq.entities.custom.v1.ListRelationshipsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | Return relationships for the given entity\_id. Either upstream or downstream. Leave empty to return every relationship in the workspace: an edge hangs off endpoints a caller may not know up front, so enumerating them by id requires already knowing every entity an edge can touch. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every matching relationship in one response — there is no server-side default page size. Set page\_size to bound the response; pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListRelationshipsResponse
(synq.entities.custom.v1.ListRelationshipsResponse)
| Field | Type | Description |
| - | - | - |
| relationships | [repeated Relationship](#relationship) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. Repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty to walk the whole result set. |
### Relationship
(synq.entities.custom.v1.Relationship)
| Field | Type | Description |
| - | - | - |
| upstream | [ Identifier](#identifier) | none |
| downstream | [ Identifier](#identifier) | none |
### RelationshipWriteResult
(synq.entities.custom.v1.RelationshipWriteResult)
RelationshipWriteResult reports what one requested relationship did to the
stored graph.
| Field | Type | Description |
| - | - | - |
| relationship | [ Relationship](#relationship) | The relationship this result answers for, as sent, with `entity_id` filled in on both endpoints. |
An edge is held between the two entities the endpoints resolve to, not between the identifiers as written, and one entity accepts several spellings — a warehouse name that differs only in case or punctuation usually names the same table. So compare these two ids, rather than the identifiers you sent, to tell whether two relationships are the same edge. |
\| outcome | [ RelationshipWriteOutcome](#relationshipwriteoutcome) | What the write did to that edge. |
### UpsertRelationshipsRequest
(synq.entities.custom.v1.UpsertRelationshipsRequest)
| Field | Type | Description |
| - | - | - |
| relationships | [repeated Relationship](#relationship) | none |
### UpsertRelationshipsResponse
(synq.entities.custom.v1.UpsertRelationshipsResponse)
| Field | Type | Description |
| - | - | - |
| results | [repeated RelationshipWriteResult](#relationshipwriteresult) | One result per relationship in the request, in request order. |
### Execution
(synq.entities.custom.v1.Execution)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | none |
| status | [ ExecutionStatus](#executionstatus) | none |
| message | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| finished\_at | [ Timestamp](#timestamp) | none |
| annotations | [repeated Annotation](#annotation) | none |
| extras | [repeated ExecutionExtra](#executionextra) | none |
### ExecutionExtra
(synq.entities.custom.v1.ExecutionExtra)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) extra.executed\_sql | [ string](#string) | none |
### LogEntry
(synq.entities.custom.v1.LogEntry)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | none |
| message | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| finished\_at | [ Timestamp](#timestamp) | none |
| annotations | [repeated Annotation](#annotation) | none |
### UpsertExecutionRequest
(synq.entities.custom.v1.UpsertExecutionRequest)
| Field | Type | Description |
| - | - | - |
| execution | [ Execution](#execution) | none |
### UpsertExecutionResponse
(synq.entities.custom.v1.UpsertExecutionResponse)
### UpsertLogEntryRequest
(synq.entities.custom.v1.UpsertLogEntryRequest)
| Field | Type | Description |
| - | - | - |
| log\_entry | [ LogEntry](#logentry) | none |
### UpsertLogEntryResponse
(synq.entities.custom.v1.UpsertLogEntryResponse)
### DeleteTypeRequest
(synq.entities.custom.v1.DeleteTypeRequest)
| Field | Type | Description |
| - | - | - |
| type\_id | [ int32](#int32) | Identifier of the custom entity type to delete. |
### DeleteTypeResponse
(synq.entities.custom.v1.DeleteTypeResponse)
### ListTypesRequest
(synq.entities.custom.v1.ListTypesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every type in one response — there is no server-side default page size. Set page\_size to bound the response; pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListTypesResponse
(synq.entities.custom.v1.ListTypesResponse)
| Field | Type | Description |
| - | - | - |
| types | [repeated Type](#type) | List of entities. |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. Repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty to walk the whole workspace. |
### UpsertTypeRequest
(synq.entities.custom.v1.UpsertTypeRequest)
| Field | Type | Description |
| - | - | - |
| type | [ Type](#type) | Entity to create or update. |
### UpsertTypeResponse
(synq.entities.custom.v1.UpsertTypeResponse)
### DeleteEntityFeatureRequest
(synq.entities.custom.v1.DeleteEntityFeatureRequest)
| Field | Type | Description |
| - | - | - |
| entity\_id | [ Identifier](#identifier) | Identifier of the entity to delete feature from. |
| feature\_id | [ string](#string) | Identifier of the feature to delete. |
### DeleteEntityFeatureResponse
(synq.entities.custom.v1.DeleteEntityFeatureResponse)
### Feature
(synq.entities.custom.v1.Feature)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Immutable workspace identifier that this entity belongs to. |
| entity\_id | [ Identifier](#identifier) | The identifier of the entity to create or update feature for. |
| feature\_id | [ string](#string) | Identifier of the feature. Can be any string, e.g. "sql" for SQL feature. Use same identifier to update the feature. Do not use random identifier as it will create a new feature on every update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feature.sql\_definition | [ SqlDefinition](#sqldefinition) | Only one feature per entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feature.git\_file\_reference | [ GitFileReference](#gitfilereference) | Multiple features per entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feature.code | [ Code](#code) | Multiple features per entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feature.schema | [ Schema](#schema) | Only one feature per entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feature.check\_category | [ CheckCategory](#checkcategory) | Only one feature per entity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) feature.column\_lineage | [ ColumnLineage](#columnlineage) | Only one feature per entity. |
| created\_at | [ Timestamp](#timestamp) | Timestamp when the entity was created. |
| updated\_at | [ Timestamp](#timestamp) | Timestamp when the entity was last updated. |
| deleted\_at | [ Timestamp](#timestamp) | Timestamp when the entity was deleted. If this is set, the entity is considered deleted. |
### ListEntityFeaturesRequest
(synq.entities.custom.v1.ListEntityFeaturesRequest)
| Field | Type | Description |
| - | - | - |
| entity\_id | [ Identifier](#identifier) | Identifier of the entity to get features for. Leave unset to list the features of every custom entity in the workspace. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every matching feature in one response — there is no server-side default page size. Set page\_size to bound the response; pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListEntityFeaturesResponse
(synq.entities.custom.v1.ListEntityFeaturesResponse)
| Field | Type | Description |
| - | - | - |
| features | [repeated Feature](#feature) | List of features for the entity. |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. Repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty to walk the whole result set. |
### UpsertEntityFeatureRequest
(synq.entities.custom.v1.UpsertEntityFeatureRequest)
| Field | Type | Description |
| - | - | - |
| feature | [ Feature](#feature) | The feature to create or update. |
### UpsertEntityFeatureResponse
(synq.entities.custom.v1.UpsertEntityFeatureResponse)
### DeleteEntitiesGroupRequest
(synq.entities.custom.v1.DeleteEntitiesGroupRequest)
| Field | Type | Description |
| - | - | - |
| group\_id | [ string](#string) | none |
### DeleteEntitiesGroupResponse
(synq.entities.custom.v1.DeleteEntitiesGroupResponse)
### Group
(synq.entities.custom.v1.Group)
Group is a collection of entities.
| Field | Type | Description |
| - | - | - |
| group\_id | [ string](#string) | Identifier of the group. |
| entity\_ids | [repeated Identifier](#identifier) | Identifiers of entities that belong to the group. |
| created\_at | [ Timestamp](#timestamp) | Timestamp when the group was created. |
| updated\_at | [ Timestamp](#timestamp) | Timestamp when the group was last updated. |
### ListEntityGroupsRequest
(synq.entities.custom.v1.ListEntityGroupsRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every group in one response — there is no server-side default page size. A group carries every one of its member identifiers, so a workspace with large groups is worth paging even though the group count is small. Pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListEntityGroupsResponse
(synq.entities.custom.v1.ListEntityGroupsResponse)
| Field | Type | Description |
| - | - | - |
| entities\_groups | [repeated Group](#group) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. Repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty to walk the whole workspace. |
### UpsertEntitiesGroupRequest
(synq.entities.custom.v1.UpsertEntitiesGroupRequest)
| Field | Type | Description |
| - | - | - |
| group | [ Group](#group) | none |
### UpsertEntitiesGroupResponse
(synq.entities.custom.v1.UpsertEntitiesGroupResponse)
| Field | Type | Description |
| - | - | - |
| deleted\_ids | [repeated Identifier](#identifier) | none |
### ColumnEdge
(synq.entities.custom.features.v1.ColumnEdge)
ColumnEdge is one column of one upstream entity feeding one column of the
entity this feature belongs to.
| Field | Type | Description |
| - | - | - |
| upstream | [ Identifier](#identifier) | The entity the data comes from. Any entity: another custom entity, or a table, model or dashboard from a connected platform. An upstream that does not exist yet is remembered and the edge appears once it does. |
| upstream\_column | [ string](#string) | Column of the upstream entity the data comes from. |
| column | [ string](#string) | Column of this entity the data flows into. |
### ColumnLineage
(synq.entities.custom.features.v1.ColumnLineage)
ColumnLineage declares column-level lineage flowing INTO a custom entity, for
the cases SQL cannot express: an upstream that is itself a custom entity, or a
component whose transformation is not written in SQL at all.
Use it when `SqlDefinition` does not fit — when there is no SQL to give at
all, or when you would rather state the result than have it derived. A
component whose transformation is written in Python, or a hand-maintained
mapping, has no SQL for a parser to read.
If the component DOES have SQL, prefer `SqlDefinition`: a name in it that is
not a database object — another custom entity, a queue, a service — is bound
to the entity it stands for with `SqlDefinition.references`, and the column
lineage is then derived from the SQL and stays correct as the SQL changes.
The declaration is complete and replaces the previous one whole: an edge
missing from a write is withdrawn, and deleting the feature withdraws all of
them. Only one ColumnLineage feature is allowed per entity, so re-writing it
under the same `feature_id` is how it is edited.
Declaring a column edge also declares the table-level edge it implies, so the
entity appears downstream of each upstream named here whether or not a
Relationship says so too. Column lineage is only ever shown along a
table-level edge, which is why the two travel together.
An entity that has BOTH this feature and a `SqlDefinition` is served from this
declaration in the SQL-lineage view — an explicit statement takes precedence
over lineage derived from SQL. Declare column lineage here only for what the
SQL cannot say, or state the entity's whole column lineage here and treat this
as the authority for it.
| Field | Type | Description |
| - | - | - |
| state\_at | [ Timestamp](#timestamp) | Time when this lineage was declared, will default to `now` if not set. A later declaration replaces an earlier one, so an explicit time is how a backfill avoids overwriting a fresher declaration. |
| edges | [repeated ColumnEdge](#columnedge) | Column-level edges into this entity. Every edge names one upstream entity and one column on each side. |
### GitFileReference
(synq.entities.custom.features.v1.GitFileReference)
GitFileReference represents a reference to a file in a git repository.
| Field | Type | Description |
| - | - | - |
| repository\_url | [ string](#string) | URL of the git repository. Preferably SSH clone URL. |
| branch\_name | [ string](#string) | Name of the branch in the git repository. |
| file\_path | [ string](#string) | Path to the file in the git repository. |
### Schema
(synq.entities.custom.features.v1.Schema)
Schema represents a schema of a table like entity
| Field | Type | Description |
| - | - | - |
| state\_at | [ Timestamp](#timestamp) | Time when the schema was defined, will default to `now` if not set. |
| columns | [repeated SchemaColumn](#schemacolumn) | Columns of the schema Columns are ordered by ordinal\_position, it has to be defined for all or none of the columns, when not defined, ordinal positions are attached according to the order of columns in the list. |
### Code
(synq.entities.custom.features.v1.Code)
Code feature represents a code snippet associated with the entity.
If support for code parsing and dependency extraction is needed use [SqlDefinition](sql_definition.proto).
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Might be e.g. name of the file or a function. |
| code\_type | [ CodeType](#codetype) | Type of the code. |
| content | [ string](#string) | Content of the code. Displayed in the UI. |
### SqlDefinition
(synq.entities.custom.features.v1.SqlDefinition)
SqlDefinition is a feature that allows to define lineage via SQL for a custom entity.
Used to generate dependencies and other metadata. If parsed successfully,
it will provide a code and column level lineage. Can be used to define simplified transformation
of data which component performs using e.g. `INSERT INTO foo SELECT * FROM bar`.
| Field | Type | Description |
| - | - | - |
| state\_at | [ Timestamp](#timestamp) | Time when the sql was defined, will default to `now` if not set. |
| dialect | [ SqlDialect](#sqldialect) | SQL dialect used in the SQL. |
| sql | [ string](#string) | Final form of the SQL as executed in the database/data warehouse. Must be a valid SQL in the selected dialect. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database\_context.database\_context | [optional DatabaseContext](#databasecontext) | Optional database context for resolving unqualified table references in the SQL. When provided, helps CLL resolve schema-qualified references (e.g. `PROD.TABLE_NAME`) to the correct database instance and catalog. |
| references | [repeated SqlTableReference](#sqltablereference) | Names in this SQL that are not database objects, each bound to the entity it stands for. A reference the SQL makes to one of these names resolves to that entity instead of being looked up in the database and dropped. |
Use it for the upstreams and downstreams a database cannot address: another custom entity, a queue, a service, an API. A name that IS a database object needs no binding — it resolves on its own.
The bindings are part of the definition and replace with it: a binding dropped from a later write is gone, and the lineage it produced is withdrawn. |
### SqlTableReference
(synq.entities.custom.features.v1.SqlTableReference)
SqlTableReference binds one name, as the SQL writes it, to the entity that name stands for.
| Field | Type | Description |
| - | - | - |
| instance\_name | [ string](#string) | The name as the SQL writes it. Matched case-insensitively; a part left empty defaults from the definition's `database_context`, exactly as an unqualified table reference in the SQL does. So a binding of just `object_name: "orders_topic"` under a definition whose database is `kafka` matches both `FROM kafka.orders_topic` and `FROM orders_topic`. |
| database\_name | [ string](#string) | none |
| schema\_name | [ string](#string) | none |
| object\_name | [ string](#string) | none |
| entity | [ Identifier](#identifier) | The entity this name stands for. Any entity: another custom entity, or a table, model or dashboard from a connected platform. An entity that does not exist yet is remembered and the edge appears once it does. |
### CheckCategory
(synq.entities.custom.features.v1.CheckCategory)
Checks feature indicates that the entity is testing in some way different entity.
To define the kind of the check use the `kind` field. The `package` field is optional.
It is used to categorize the entities which check other entities.
| Field | Type | Description |
| - | - | - |
| package | [ string](#string) | Grouping of check kinds. e.g. "gcs", "sql", "queue" |
| kind | [ string](#string) | Kind of the check. e.g "not\_null", "unique", "foreign\_key", "row\_count". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Explicit technical category of the check — what kind of check this is, mechanically (e.g. "nullness", "uniqueness", "freshness"), so the same check expressed on another platform shares one category. |
Presence is meaningful on update: omit the field to leave the current value untouched, send an empty string to clear it, send a non-empty string to set or replace it. Leaving it unset means the check is uncategorised for this dimension and the workspace's categorisation rules decide it from package + kind; the `quality.check_category` annotation on the entity is read as the lower-precedence fallback. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | Explicit governance category of the check — what the check is for, the data-quality dimension a governance function tracks coverage against (e.g. "Completeness", "Accuracy", "Timeliness").
Resolves independently of `category` and follows the same presence semantics: omitted leaves it unchanged, empty clears it, non-empty sets it. Unset means uncategorised for this dimension; the `quality.governance_category` annotation on the entity is the lower-precedence fallback. |
### BrowseRequest
(synq.entities.folders.v1.BrowseRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) parent.parent\_folder\_entity\_id | [ string](#string) | Opaque id of the parent folder to browse into (from `Folder.folder_entity_id`). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) parent.parent\_ref | [ FolderRef](#folderref) | Structured address of the parent folder to browse into. |
| filter | [ Query](#query) | Optional filter. When set, the returned `member_entity_ids` are limited to entities matching this query. |
### BrowseResponse
(synq.entities.folders.v1.BrowseResponse)
| Field | Type | Description |
| - | - | - |
| folders | [repeated Folder](#folder) | Direct sub-folders of the browsed location (or the roots, when no `parent` was given). |
| member\_entity\_ids | [repeated string](#string) | Opaque entity ids of the direct, non-folder members of the browsed folder. Empty when browsing the roots. Shallow by default — members of sub-folders are not included unless `depth` expanded them. |
### GetFolderOfRequest
(synq.entities.folders.v1.GetFolderOfRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Opaque entity ids to look up. Ids that do not exist, or that live in no folder, are absent from the response. |
### GetFolderOfResponse
(synq.entities.folders.v1.GetFolderOfResponse)
| Field | Type | Description |
| - | - | - |
| breadcrumbs\_by\_entity\_id | [map BreadcrumbsByEntityIdEntry](#breadcrumbsbyentityidentry) | Requested entity id -> its containing breadcrumb(s). |
### GetFolderOfResponse.Breadcrumb
(synq.entities.folders.v1.GetFolderOfResponse.Breadcrumb)
One containing breadcrumb: the folders from a root down to (and including)
the entity's immediate parent, ordered root first.
| Field | Type | Description |
| - | - | - |
| folders | [repeated Folder](#folder) | none |
### GetFolderOfResponse.Breadcrumbs
(synq.entities.folders.v1.GetFolderOfResponse.Breadcrumbs)
All breadcrumbs for one entity. Most entities have a single breadcrumb; an
entity placed in several hierarchies has one per hierarchy.
| Field | Type | Description |
| - | - | - |
| breadcrumbs | [repeated Breadcrumb](#breadcrumb) | none |
### GetFolderOfResponse.BreadcrumbsByEntityIdEntry
(synq.entities.folders.v1.GetFolderOfResponse.BreadcrumbsByEntityIdEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Breadcrumbs](#breadcrumbs) | none |
### ListMembersRequest
(synq.entities.folders.v1.ListMembersRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) folder.folder\_entity\_id | [ string](#string) | Opaque id of the folder (from `Folder.folder_entity_id`). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) folder.folder\_ref | [ FolderRef](#folderref) | Structured address of the folder. |
| deep | [ bool](#bool) | When true, include entities in all nested sub-folders. When false (default), return only the entities placed directly in this folder. |
| types | [repeated EntityType](#entitytype) | Optional entity-type filter. Empty returns all types. |
| filter | [ Query](#query) | Optional query filter. When set, only members matching this query are returned. |
| pagination | [ Pagination](#pagination) | Pagination. |
### ListMembersResponse
(synq.entities.folders.v1.ListMembersResponse)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Opaque entity ids of the folder's members. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### Folder
(synq.entities.folders.v1.Folder)
Folder is one node in the folder / containment hierarchy: an integration root,
a container (database, schema, project), or a nested folder. Members that are
not themselves folders (tables, models, …) are returned as entity ids, not as
`Folder`s.
| Field | Type | Description |
| - | - | - |
| folder\_entity\_id | [ string](#string) | Opaque, authoritative id of this folder. Round-trip it verbatim as the `parent` of `Browse` or the `folder` of `ListMembers`; do not parse it. It is the canonical handle for a folder — always prefer it over rebuilding a location from `ref` when calling back in. |
| ref | [ FolderRef](#folderref) | Structured, human-friendly address of this folder. Populated when it can be derived from the surrounding hierarchy (always for `GetFolderOf`, and for `Browse` when the parent location is known). When it cannot be derived, `folder_entity_id` remains the authoritative handle. |
| name | [ string](#string) | Display name of this folder (its own leaf name, e.g. "sales"). |
| full\_name | [repeated string](#string) | Full display path from the root down to this folder (root first, leaf last), e.g. \["clickhouse-prod", "default", "sales"]. Suitable for rendering a breadcrumb. |
| type | [ EntityType](#entitytype) | Entity type of this node. Folders synthesized purely for grouping report an unspecified type; container entities that also act as folders (databases, schemas, projects) report their real type. |
| platform | [ DataPlatformType](#dataplatformtype) | Platform this folder belongs to, when known. |
| child\_folder\_count | [ uint32](#uint32) | Number of direct sub-folders. Best-effort: it may be reported as a collapsed count for lazily-loaded folders and can be 0 when the count was not loaded. |
| member\_count | [ uint32](#uint32) | Number of direct, non-folder members (entities that sit directly in this folder). Best-effort and may be 0 when not loaded. |
| role | [ FolderRole](#folderrole) | How this folder participates in browsing. |
### BatchGetEntityAnnotationsRequest
(synq.entities.annotations.v1.BatchGetEntityAnnotationsRequest)
BatchGetEntityAnnotationsRequest is the request message for the
BatchGetEntityAnnotations method.
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | The entities to read annotations for. Each entry is validated by Identifier's own rules — its `id` oneof is `required`, and protovalidate recurses into the elements of a repeated message field — so an identifier naming nothing is refused here. There is no per-item `required` to add beside it: `repeated.items.required` is unenforceable and buf lint rejects it, and it would have nothing to catch, because an entry on the wire is always a message rather than a null. |
### BatchGetEntityAnnotationsResponse
(synq.entities.annotations.v1.BatchGetEntityAnnotationsResponse)
BatchGetEntityAnnotationsResponse is the response message for the
BatchGetEntityAnnotations method.
| Field | Type | Description |
| - | - | - |
| entity\_annotations | [repeated EntityAnnotations](#entityannotations) | One entry per requested entity that carries any annotation. An entity with none is omitted rather than returned empty, so the response says which of the requested entities are annotated at all. |
### EntityAnnotations
(synq.entities.annotations.v1.EntityAnnotations)
EntityAnnotations is the annotations one entity carries.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | The entity, echoed back in the spelling it was requested in. |
| annotations | [repeated Annotation](#annotation) | Annotations the entity carries, one entry per name, ordered by name. |
Read over the entity's whole identity group rather than the one id asked for: a dbt model, the warehouse table it builds and a catalog entity virtualising the same table are one entity to a reader, and which of them an annotation was published against is a property of the producer, not of the thing being annotated. |
### ListAnnotationsRequest
(synq.entities.annotations.v1.ListAnnotationsRequest)
ListAnnotationsRequest is the request message for the ListAnnotations method.
### ListAnnotationsResponse
(synq.entities.annotations.v1.ListAnnotationsResponse)
ListAnnotationsResponse is the response message for the ListAnnotations method.
| Field | Type | Description |
| - | - | - |
| annotation\_counts | [repeated AnnotationCount](#annotationcount) | The annotation counts grouped by name and value. Spellings of a name or a value that differ only in case are counted together and returned under one of the spellings entities carry, the same for every value of a name. |
### AnnotationCount
(synq.entities.annotations.v1.AnnotationCount)
AnnotationCount represents the count of entities that have a specific annotation name-value pair.
This is used for aggregating and reporting annotation statistics across entities.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | The annotation name (key). |
| value | [ string](#string) | The annotation value. If empty, represents the count for all values of this annotation name. |
| count | [ int32](#int32) | The number of entities that have this annotation name-value pair. |
### EntityIncident
(synq.entities.status.v1.EntityIncident)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Unique identifier of the incident. |
| url | [ string](#string) | URL of the incident. |
| name | [ string](#string) | Name of the incident give by the user. |
| created\_by | [ string](#string) | Information about author of the incident. |
| created\_at | [ Timestamp](#timestamp) | Time when the incident was created. |
| updated\_by | [ string](#string) | Information about the user who last updated the incident. |
| updated\_at | [ Timestamp](#timestamp) | Time when the incident was last updated. |
| is\_resolved | [ bool](#bool) | Indicates if all issues inside the incident are resolved. |
### BatchGetIssuesStatusRequest
(synq.entities.status.v1.BatchGetIssuesStatusRequest)
BatchGetIssuesStatusRequest is the request message for the BatchGetIssues method.
| Field | Type | Description |
| - | - | - |
| requests | [repeated GetIssuesStatusRequest](#getissuesstatusrequest) | none |
### BatchGetIssuesStatusResponse
(synq.entities.status.v1.BatchGetIssuesStatusResponse)
BatchGetIssuesStatusResponse is the response message for the BatchGetIssues method.
| Field | Type | Description |
| - | - | - |
| responses | [repeated GetIssuesStatusResponse](#getissuesstatusresponse) | none |
### GetIssuesStatusRequest
(synq.entities.status.v1.GetIssuesStatusRequest)
GetIssuesStatusRequest is the request message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get status for. |
| fetch\_upstream\_status | [ bool](#bool) | Retrieve status of upstream entities. |
### GetIssuesStatusResponse
(synq.entities.status.v1.GetIssuesStatusResponse)
GetIssuesStatusResponse is the response message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity. |
| entity\_issues\_status | [ IssueStatus](#issuestatus) | Status of the entity requested. |
| entity\_issues\_summary | [ IssuesSummary](#issuessummary) | none |
| upstream\_issues\_status | [ IssueStatus](#issuestatus) | Optionally, the status of all upstream entities. |
| upstream\_issues\_summary | [ IssuesSummary](#issuessummary) | none |
### GetIssuesStatusResponse.IssuesSummary
(synq.entities.status.v1.GetIssuesStatusResponse.IssuesSummary)
| Field | Type | Description |
| - | - | - |
| total\_count | [ int32](#int32) | Number of issues for the entity. |
| warn\_count | [ int32](#int32) | Number of issues for the entity with status 'warn'. |
| error\_count | [ int32](#int32) | Number of issues for the entity with status 'error'. |
| critical\_count | [ int32](#int32) | Number of issues for the entity with status 'critical'. |
### BatchGetIncidentsRequest
(synq.entities.status.v1.BatchGetIncidentsRequest)
BatchGetIncidentsRequest is the request message for the BatchGetIssues method.
| Field | Type | Description |
| - | - | - |
| requests | [repeated GetIncidentsRequest](#getincidentsrequest) | none |
### BatchGetIncidentsResponse
(synq.entities.status.v1.BatchGetIncidentsResponse)
BatchGetIncidentsResponse is the response message for the BatchGetIssues method.
| Field | Type | Description |
| - | - | - |
| responses | [repeated GetIncidentsResponse](#getincidentsresponse) | none |
### GetIncidentsRequest
(synq.entities.status.v1.GetIncidentsRequest)
GetIncidentsRequest is the request message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get incidents for. |
| fetch\_upstream\_incidents | [ bool](#bool) | Retrieve incidents of upstream entities. |
### GetIncidentsResponse
(synq.entities.status.v1.GetIncidentsResponse)
GetIncidentsResponse is the response message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity. |
| entity\_open\_incidents | [repeated EntityIncident](#entityincident) | Open incident associated with the entity requested. |
| upstream\_open\_incidents | [repeated EntityIncident](#entityincident) | Open incidents associated with the upstream entities if requested. |
### Type
(synq.entities.v1.Type)
Custom entity type that is used to categorize custom entities.
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Immutable workspace identifier that this custom entity type belongs to. |
| type\_id | [ int32](#int32) | none |
| name | [ string](#string) | Short label for the custom entity type, shown beside the type's icon, in type filters, and on the type badge of every entity of this type. |
It is the only name a custom type has, so keep it short enough to read as a caption rather than a description — "Kafka Topic", not a sentence. |
\| svg\_icon | [ bytes](#bytes) | SVG icon of the custom entity type that is displayed to the user. |
\| svg\_icon\_id | [ string](#string) | Coalesce Quality identifier used for CDN purposes, read-only. |
\| created\_at | [ Timestamp](#timestamp) | Timestamp when the custom entity type was created. |
\| updated\_at | [ Timestamp](#timestamp) | Timestamp when the custom entity type was last updated. |
\| deleted\_at | [ Timestamp](#timestamp) | Timestamp when the custom entity type was deleted. If this is set, the custom entity type is considered deleted. |
\| traits | [ TypeTraits](#typetraits) | Behavioral traits declared on this type. Every entity of the type inherits them: declaring is\_model, for example, makes its entities rank and behave as transformation models (canonical-entity ranking, monitorability, lineage), the same way a dbt model does. Unset means no declared traits (all false). |
### Schema
(synq.entities.v1.Schema)
Schema represents a schema of a table like entity
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Immutable workspace identifier that this entity belongs to. |
| id | [ Identifier](#identifier) | Unique identifier for this entity. |
| synq\_path | [ string](#string) | Coalesce Quality Path to the entity. This is a unique path that can be used to access the entity in the Coalesce Quality UI. |
| state\_at | [ Timestamp](#timestamp) | Time when the schema was defined. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Optional description of the schema, might differ from the entity itself |
| columns | [repeated SchemaColumn](#schemacolumn) | Columns of the schema |
### SchemaColumn
(synq.entities.v1.SchemaColumn)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human readable name of the column as present in dbt or data warehouse. |
| native\_type | [ string](#string) | Native data type of the column as present in data warehouse. |
| description | [ string](#string) | Description of the column |
| ordinal\_position | [ int32](#int32) | Ordinal position of the column in the table, starting from 1 |
| is\_struct | [ bool](#bool) | Indicates that the column type could be used as a struct/json in a data warehouse |
| is\_repeated | [ bool](#bool) | Indicates that the column is a repeated field in a data warehouse (e.g. array) |
| fields | [repeated SchemaColumnField](#schemacolumnfield) | Fields inside of the struct/record like column |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_precision.precision | [optional int64](#int64) | Declared precision of an exact numeric column (NUMERIC, DECIMAL, NUMBER), in decimal digits. Set whichever category the column reads as, so a NUMBER(38,0) is NUMERIC\_CATEGORY\_INTEGER with precision 38 and scale 0. Unset for every other column, and for an exact numeric whose size the data warehouse does not report. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scale.scale | [optional int64](#int64) | Declared scale of an exact numeric column, set together with precision. Unset is not a scale of 0: it means the scale is not known. |
| numeric\_category | [ NumericCategory](#numericcategory) | What kind of number the column holds. Unspecified for a non-numeric column, and for one whose type is not recognised. |
### SchemaColumnField
(synq.entities.v1.SchemaColumnField)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human readable name of the column as present in dbt or data warehouse. |
| native\_type | [ string](#string) | Native data type of the column as present in data warehouse. |
| description | [ string](#string) | Description of the column |
| ordinal\_position | [ int32](#int32) | Ordinal position of the column in the struct, starting from 1 |
| is\_struct | [ bool](#bool) | Indicates that the field type could be used as a struct/json in a data warehouse |
| is\_repeated | [ bool](#bool) | Indicates that the field is a repeated field in a data warehouse (e.g. array) |
| fields | [repeated SchemaColumnField](#schemacolumnfield) | Fields inside of the struct/record like field |
### Annotation
(synq.entities.v1.Annotation)
Annotations can be used to annotate any entity with a key:value pair.
These annotations can be used for filtering and searching entities.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | String key for the annotation. |
| values | [repeated string](#string) | Optional list of values that the annotation can carry. |
A value is something entities are filtered and searched by, not free-form metadata, so keep it to a label a person would pick out of a list. The limit is set to hold an identifier or a link the annotated system generated — truncating one of those produces a value that no longer resolves, and no longer matches the filter it exists for. Put prose in the entity description instead. |
### AirflowDagIdentifier
(synq.entities.v1.AirflowDagIdentifier)
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Coalesce Quality integration\_id that identifies the Airflow instance |
| dag\_id | [ string](#string) | Airflow dag\_id that identifies the DAG |
### AirflowTaskIdentifier
(synq.entities.v1.AirflowTaskIdentifier)
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Coalesce Quality integration\_id that identifies the Airflow instance |
| dag\_id | [ string](#string) | Airflow dag\_id that identifies the DAG |
| task\_id | [ string](#string) | Airflow task\_id that identifies the task within the DAG |
### AthenaTableIdentifier
(synq.entities.v1.AthenaTableIdentifier)
| Field | Type | Description |
| - | - | - |
| instance | [ string](#string) | AWS account and region the Athena workgroup runs in, formatted as "\.\" (e.g. "123456789012.eu-west-1"). Auto-derived from sts:GetCallerIdentity at integration setup time. |
| catalog | [ string](#string) | Glue Data Catalog name. Almost always "AwsDataCatalog" — the default catalog for Athena unless the customer registered a federated catalog. |
| database | [ string](#string) | Glue database (the namespace users typically filter on). |
| table | [ string](#string) | Glue table or view name. |
### BigqueryTableIdentifier
(synq.entities.v1.BigqueryTableIdentifier)
| Field | Type | Description |
| - | - | - |
| project | [ string](#string) | BigQuery project |
| dataset | [ string](#string) | BigQuery dataset id |
| table | [ string](#string) | BigQuery table name |
### ClickhouseTableIdentifier
(synq.entities.v1.ClickhouseTableIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Clickhouse hostname without port |
| schema | [ string](#string) | Clickhouse database |
| table | [ string](#string) | Clickhouse table |
### CustomIdentifier
(synq.entities.v1.CustomIdentifier)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id that identifies the custom entity The Id should be unique within the custom entity Identifier. |
### DatabricksTableIdentifier
(synq.entities.v1.DatabricksTableIdentifier)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | URL of Databricks workspace |
| catalog | [ string](#string) | Databricks catalog |
| schema | [ string](#string) | Databricks schema |
| table | [ string](#string) | Databricks table or view |
### DataproductIdentifier
(synq.entities.v1.DataproductIdentifier)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Dataproduct id that identifies the Dataproduct |
### Db2TableIdentifier
(synq.entities.v1.Db2TableIdentifier)
IBM Db2 for Linux, UNIX and Windows table identifier.
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Host name of the Db2 server |
| database | [ string](#string) | Database name or alias, as in `CONNECT TO ` |
| schema | [ string](#string) | Db2 schema (e.g. "DB2INST1") |
| table | [ string](#string) | Db2 table or view |
### DbtCloudNodeIdentifier
(synq.entities.v1.DbtCloudNodeIdentifier)
| Field | Type | Description |
| - | - | - |
| project\_id | [ string](#string) | Your dbt Cloud project id |
| account\_id | [ string](#string) | Your dbt Cloud account id |
| node\_id | [ string](#string) | Dbt node\_id that identifies one of dbt DAG nodes (model, test, etc) |
### DbtCoreNodeIdentifier
(synq.entities.v1.DbtCoreNodeIdentifier)
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Opaque identifier of the dbt Core project. Usually the Coalesce Quality integration id (a UUID), but may also be the dbt project name from manifest.json when the integration is configured to identify assets by project name. Treat it as an opaque string, not a UUID. |
| node\_id | [ string](#string) | Dbt node\_id that identifies one of dbt DAG nodes (model, test, etc) |
### FabricTableIdentifier
(synq.entities.v1.FabricTableIdentifier)
Microsoft Fabric warehouse/lakehouse table identifier. Fabric is
workspace-scoped with cross-database three-part \[database].\[schema].\[table]
T-SQL names.
| Field | Type | Description |
| - | - | - |
| instance | [ string](#string) | Workspace SQL analytics endpoint host, e.g. "\.datawarehouse.fabric.microsoft.com". |
| database | [ string](#string) | Fabric warehouse / lakehouse (database). |
| schema | [ string](#string) | Schema within the database. |
| table | [ string](#string) | Table or view name. |
| workspace\_id | [ string](#string) | Fabric workspace GUID, decoded from the endpoint host in `instance`. Optional and informational: the table is addressed by instance/database/schema/table, so leave it empty if you don't have it — it does not affect how the identifier resolves. |
| tenant\_id | [ string](#string) | Entra (Azure AD) tenant GUID, decoded from the endpoint host in `instance`. Optional and informational, like workspace\_id. |
### Identifier
(synq.entities.v1.Identifier)
Identifier is a unique reference to an entity in Coalesce Quality system. Entity identifiers
are designed to closely mimic identifiers used by data platforms and tools. To construct
an identifier, you need to know the kind of the entity and the ids that you would normally
use to identify it in the data platform or tool. For example, to identify a table in BigQuery,
you would need to know the project, dataset, and table names.
Every identifier the API RETURNS also carries `entity_id`, the entity's opaque
id. Read it when you just need to name the entity again — it is one string for
every platform, where the structured variants below only cover the platforms
that have one.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | Opaque id of the entity, set on every identifier the API returns. Round-trip it verbatim; the format is not part of this contract and may change, and it is never constructed by hand. |
OUTPUT ONLY. On a request this field is ignored — the structured variant is what resolves. That is what lets you hand an identifier straight back unchanged, or edit its structured fields, without the two disagreeing about which entity you meant. To name an entity BY opaque id on a request, set the `synq_path` variant to that id: it carries exactly this string, and it is also how an entity whose platform has no structured variant is named. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.dbt\_core\_node | [ DbtCoreNodeIdentifier](#dbtcorenodeidentifier) | Dbt node that identifies one of dbt DAG nodes (model, test, etc) in dbt Core project |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.dbt\_cloud\_node | [ DbtCloudNodeIdentifier](#dbtcloudnodeidentifier) | Dbt node that identifies one of dbt DAG nodes (model, test, etc) in dbt Cloud project |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.bigquery\_table | [ BigqueryTableIdentifier](#bigquerytableidentifier) | BigQuery table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.snowflake\_table | [ SnowflakeTableIdentifier](#snowflaketableidentifier) | Snowflake table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.redshift\_table | [ RedshiftTableIdentifier](#redshifttableidentifier) | Redshift table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.postgres\_table | [ PostgresTableIdentifier](#postgrestableidentifier) | Postgres table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.mysql\_table | [ MysqlTableIdentifier](#mysqltableidentifier) | Mysql table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.clickhouse\_table | [ ClickhouseTableIdentifier](#clickhousetableidentifier) | Clickhouse table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.airflow\_dag | [ AirflowDagIdentifier](#airflowdagidentifier) | Airflow DAG identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.airflow\_task | [ AirflowTaskIdentifier](#airflowtaskidentifier) | Airflow task identifier within a given DAG |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.custom | [ CustomIdentifier](#customidentifier) | Custom identifier to be used with all custom created entities |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.dataproduct | [ DataproductIdentifier](#dataproductidentifier) | Dataproduct identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.synq\_path | [ SynqPathIdentifier](#synqpathidentifier) | SynqPath identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.databricks\_table | [ DatabricksTableIdentifier](#databrickstableidentifier) | Databricks table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.trino\_table | [ TrinoTableIdentifier](#trinotableidentifier) | Trino table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.sql\_mesh\_model | [ SqlMeshModelIdentifier](#sqlmeshmodelidentifier) | SQLMesh Model identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.sql\_mesh\_audit | [ SqlMeshAuditIdentifier](#sqlmeshauditidentifier) | SQLMesh Audit identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.monitor | [ MonitorIdentifier](#monitoridentifier) | Monitor identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.mssql\_table | [ MssqlTableIdentifier](#mssqltableidentifier) | SQL Server table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.oracle\_table | [ OracleTableIdentifier](#oracletableidentifier) | Oracle table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.athena\_table | [ AthenaTableIdentifier](#athenatableidentifier) | Amazon Athena (Glue) table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.saved\_view | [ SavedViewIdentifier](#savedviewidentifier) | Saved view identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.fabric\_table | [ FabricTableIdentifier](#fabrictableidentifier) | Microsoft Fabric table identifier |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) id.db2\_table | [ Db2TableIdentifier](#db2tableidentifier) | IBM Db2 LUW table identifier |
### MonitorIdentifier
(synq.entities.v1.MonitorIdentifier)
| Field | Type | Description |
| - | - | - |
| monitored\_id | [ Identifier](#identifier) | Identifier of the monitored entity |
| monitor\_id | [ string](#string) | Identifier of the monitor |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_segment.segment | [optional string](#string) | Optional monitor segmentation identifier |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_integration\_id.integration\_id | [optional string](#string) | Coalesce Quality integration\_id of the monitored identifier |
### MssqlTableIdentifier
(synq.entities.v1.MssqlTableIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Hostname of the SQL Server instance |
| database | [ string](#string) | SQL Server database |
| schema | [ string](#string) | SQL Server schema (e.g. "dbo") |
| table | [ string](#string) | SQL Server table or view |
### MysqlTableIdentifier
(synq.entities.v1.MysqlTableIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Mysql hostname without port |
| schema | [ string](#string) | Mysql database |
| table | [ string](#string) | Mysql table |
### OracleTableIdentifier
(synq.entities.v1.OracleTableIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Hostname of the Oracle instance |
| service\_name | [ string](#string) | Oracle service name (PDB) |
| schema | [ string](#string) | Oracle schema (= owning user) |
| table | [ string](#string) | Oracle table or view |
### OwnerIdentifier
(synq.entities.v1.OwnerIdentifier)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Owner id that identifies the Owner |
### PostgresTableIdentifier
(synq.entities.v1.PostgresTableIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Postgres hostname without port |
| database | [ string](#string) | Postgres database |
| schema | [ string](#string) | Postgres schema |
| table | [ string](#string) | Postgres table |
### RedshiftTableIdentifier
(synq.entities.v1.RedshiftTableIdentifier)
| Field | Type | Description |
| - | - | - |
| cluster | [ string](#string) | Redshift cluster |
| database | [ string](#string) | Redshift database |
| schema | [ string](#string) | Redshift schema |
| table | [ string](#string) | Redshift table |
### SavedViewIdentifier
(synq.entities.v1.SavedViewIdentifier)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Saved view id (client-supplied UUID) that identifies the saved view |
### SnowflakeTableIdentifier
(synq.entities.v1.SnowflakeTableIdentifier)
| Field | Type | Description |
| - | - | - |
| account | [ string](#string) | Snowflake account |
| database | [ string](#string) | Snowflake database |
| schema | [ string](#string) | Snowflake schema |
| table | [ string](#string) | Snowflake table |
### SqlMeshAuditIdentifier
(synq.entities.v1.SqlMeshAuditIdentifier)
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Coalesce Quality integration\_id that identifies the dbt Core project |
| fqn | [ string](#string) | SQLMesh model fully qualified name |
| audit\_id | [ string](#string) | Identifier of the audit |
### SqlMeshModelIdentifier
(synq.entities.v1.SqlMeshModelIdentifier)
| Field | Type | Description |
| - | - | - |
| integration\_id | [ string](#string) | Coalesce Quality integration\_id that identifies the dbt Core project |
| fqn | [ string](#string) | SQLMesh model fully qualified name |
### SynqPathIdentifier
(synq.entities.v1.SynqPathIdentifier)
| Field | Type | Description |
| - | - | - |
| path | [ string](#string) | Coalesce Quality path that identifies the Coalesce Quality entity, needs to be one of supported paths |
### TrinoTableIdentifier
(synq.entities.v1.TrinoTableIdentifier)
| Field | Type | Description |
| - | - | - |
| host | [ string](#string) | Hostname of the Trino instance |
| catalog | [ string](#string) | Trino catalog |
| schema | [ string](#string) | Trino schema |
| table | [ string](#string) | Trino table or view |
### Code
(synq.entities.v1.Code)
Code represents a piece of code or configuration that can be processed by Coalesce Quality.
This message is used to store and manage various types of code artifacts, such as SQL queries,
Python scripts, dbt models, and other code configurations. Each code piece is associated with
a specific entity that defines or owns it, and includes metadata about its type and state.
| Field | Type | Description |
| - | - | - |
| definer\_identifier | [ Identifier](#identifier) | Identifier of the entity that defines or owns this code piece. This could be a dbt model, SQLMesh model, or any other entity type that contains code. |
| definer\_synq\_path | [ string](#string) | Coalesce Quality Path to the entity. This is a unique path that can be used to access the entity in the Coalesce Quality UI. |
| definer\_entity\_type | [ EntityType](#entitytype) | Information about entity type of the entity defining code |
| state\_at | [ Timestamp](#timestamp) | Timestamp indicating when this specific version of the code was captured or last modified. This helps track the evolution of code over time. |
| code\_type | [ CodeType](#codetype) | Type of code being stored, which determines how Coalesce Quality will process and analyze it. This can be SQL, Python, dbt, SQLMesh, or other supported code types. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sql\_dialect.sql\_dialect | [optional SqlDialect](#sqldialect) | SQL dialect used in the code, if the code\_type is SQL. This helps Coalesce Quality understand the specific SQL syntax and features being used. |
| content | [ string](#string) | The actual content of the code. This can be SQL queries, Python scripts, dbt models, or any other supported code format. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_code\_name.code\_name | [optional string](#string) | Optional human-readable name for the code piece that can be used for display purposes. Might also indicate name of the file. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_file\_path.file\_path | [optional string](#string) | Optional path in e.g. git or project directory which is source of the code |
| definer\_platform | [ DataPlatformType](#dataplatformtype) | Data platform of the entity that defines this code — dbt, SQLMesh, Snowflake, and so on. `definer_entity_type` says what kind of thing defines the code; this says which tool it belongs to. |
Use it to order the code an entity has when it has more than one piece: a warehouse table modelled by a transformation framework carries both the framework's source and the warehouse's own DDL, and the framework source is what a reader normally wants first. |
\| has\_execution\_error | [ bool](#bool) | True when the definer failed to produce this code the last time it ran — a model that did not compile, a routine whose body could not be read. The `content` is then the last body that was captured, which may be stale or empty; `execution_error` says what went wrong. |
\| execution\_error | [ string](#string) | Message describing the failure `has_execution_error` reports. Empty when there is no failure, and may also be empty when the producer recorded a failure without a message. |
\| is\_truncated | [ bool](#bool) | True when `content` holds only the first part of the file. Files above the ingestion size cap are stored as a prefix, and a client showing this code should say so rather than presenting the prefix as the whole body. `original_size_bytes` gives the untruncated size. |
\| original\_size\_bytes | [ uint64](#uint64) | Size in bytes of the code before any truncation. Read it together with `is_truncated`, which is what makes it meaningful: where `content` was cut, this is the size of the whole body, and a truncated body always has one. Zero where nothing measured the source, which is the normal case for code that is not read from a file and never happens for a truncated body. |
### CodeSourceError
(synq.entities.v1.CodeSourceError)
CodeSourceError reports one source that could not be read while the rest of
the response was assembled. That source's code is missing or incomplete in
`codes`; every other source in the response is complete.
Retry the same request to pick the missing source up — these are transient
by nature. A request in which every source failed returns an error instead.
| Field | Type | Description |
| - | - | - |
| source | [ CodeSource](#codesource) | Which source was unavailable. |
| message | [ string](#string) | Human-readable reason, for logging and for telling a reader why part of the code is missing. Not a stable string; do not branch on it. |
### Entity
(synq.entities.v1.Entity)
Entity is the base type for all entities in the system. Its always attached to a given
client workspace and can be uniquely identified by its id.
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Immutable workspace identifier that this entity belongs to. |
| id | [ Identifier](#identifier) | Unique identifier for this entity. |
| type\_id | [ int32](#int32) | Id of custom type or default type when 0, ignored for non-custom entities |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_entity\_type.entity\_type | [optional EntityType](#entitytype) | Information about entity type |
| name | [ string](#string) | Name of the entity that is displayed to the user. Set it to the name the entity is known by in the system it comes from, rather than shortening it to fit — the limit is set wide enough to carry a generated name whole. |
| description | [ string](#string) | Description of the entity that is displayed in relevant contexts. Can contain Markdown. |
| created\_at | [ Timestamp](#timestamp) | Timestamp when the entity was created. |
| updated\_at | [ Timestamp](#timestamp) | Timestamp when the entity was last updated. |
| deleted\_at | [ Timestamp](#timestamp) | Timestamp when the entity was deleted. If this is set, the entity is considered deleted. |
| synq\_path | [ string](#string) | Return only value. Coalesce Quality Path to the entity. This is a unique path that can be used to access the entity in the Coalesce Quality UI. |
| synq\_catalog\_url | [ string](#string) | Return only value. Coalesce Quality URL to the entity. This is a unique URL that can be used to access the entity in the Coalesce Quality UI. |
| ids | [repeated Identifier](#identifier) | Return only value. All identifiers of entities which are tightly related |
| synq\_paths | [repeated string](#string) | Return only value. Coalesce Quality Path to the all identifiers of entities which are tightly related |
| annotations | [repeated Annotation](#annotation) | Annotations attached to the entity. Annotations are key-value pairs that can be used to store metadata about the entity. |
| is\_dwh\_table | [ bool](#bool) | none |
| is\_dwh\_view | [ bool](#bool) | none |
### FolderRef
(synq.entities.v1.FolderRef)
FolderRef is the human-friendly, structured address of a folder: the entity it
hangs under (`root_entity_id`) plus the ordered folder names from that root
down to the folder (`folders`).
It is the stable way to name a folder across responses. Prefer it over the
opaque folder entity id when you want to log, display, or reconstruct a
location — the segments are the same names a user sees in the catalog, and
they survive changes to the underlying id encoding.
A `FolderRef` with an empty `folders` list names the root entity itself (a
top-level integration or container the hierarchy hangs from).
This type lives in `synq.entities.v1` (rather than a folder-specific package)
so it can be shared by both the folders navigation API and the query API
without an import cycle.
| Field | Type | Description |
| - | - | - |
| root\_entity\_id | [ string](#string) | Opaque id of the entity the folder hierarchy hangs under — an integration root or container (for example a warehouse instance, a database, or a transformation project). Take it verbatim from `FolderRef.root_entity_id` of a folder you already read, or from an entity id returned elsewhere. |
| folders | [repeated string](#string) | Ordered folder names from the root down to this folder (root first, leaf last), for example \["staging", "sales"]. Empty means the ref points at the root entity itself. Folder names are kept as separate segments (not joined with "/") because some platforms allow "/" inside a single folder name. |
### TypeTraits
(synq.entities.v1.TypeTraits)
TypeTraits are the behavioral traits a custom entity Type can declare.
Declaring a trait makes every entity of that type behave platform-wide as if
it carried the trait natively (e.g. is\_model => transformation-model ranking,
monitorability and lineage).
Every field is `optional` so an unset trait means "not declared, fall through
to the trait default" — distinct from an explicit `false`. An unset/nil message
means no declared traits (every trait at its default).
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_model.is\_model | [optional bool](#bool) | model-like: the dbt-model / Coalesce-node / SQLMesh-model analog. Default when unset: false. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_source.is\_source | [optional bool](#bool) | source-like: an external/raw input to the transformation graph. Default when unset: false. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_bi\_like.is\_bi\_like | [optional bool](#bool) | BI-like: a dashboard, report or other business-intelligence artifact. Default when unset: false. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_test\_type.is\_test\_type | [optional bool](#bool) | check-like: pairs with declarative checks. Default when unset: false. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_ignored\_in\_lineage.is\_ignored\_in\_lineage | [optional bool](#bool) | excluded from lineage rendering. Default when unset: false. |
### EntityChange
(synq.entities.v1.EntityChange)
EntityChange represents a change detected in an entity within the system.
It tracks modifications to entities like Git commits, SQL definitions, and database schemas.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ Identifier](#identifier) | Identifier of the entity on which the change was detected when available. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_entity\_synq\_path.entity\_synq\_path | [optional string](#string) | Coalesce Quality path representing entity on which the change was detected when available, |
| affected\_entities | [repeated Identifier](#identifier) | Identifiers of entities directly affected by this change. |
| affected\_entities\_synq\_paths | [repeated string](#string) | Coalesce Quality paths of entities directly affected by this change. |
| change\_id | [ string](#string) | Internal identifier of the change event. |
| change\_detected\_at | [ Timestamp](#timestamp) | Timestamp when the change was detected by Coalesce Quality. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) change\_type.git\_commit | [ GitCommitChange](#gitcommitchange) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) change\_type.sql\_definition | [ SqlDefinitionChange](#sqldefinitionchange) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) change\_type.schema | [ SchemaChange](#schemachange) | none |
### GitCommitChange
(synq.entities.v1.GitCommitChange)
GitCommitChange represents a change detected in a Git repository.
| Field | Type | Description |
| - | - | - |
| hash | [ string](#string) | Hash of the Git commit. |
| author | [ Signature](#signature) | Author information of the Git commit. |
| committer | [ Signature](#signature) | Committer information of the Git commit. |
| message | [ string](#string) | Commit message describing the changes. |
| short\_message | [ string](#string) | First non-empty line of the message |
| file\_change\_stats | [map FileChangeStatsEntry](#filechangestatsentry) | Statistics about changes made to each modified file. |
| changed\_file\_paths | [repeated string](#string) | All file paths changed in this commit (complete list). For commits with many files, this may be a large array. |
| related\_changed\_file\_paths | [repeated string](#string) | File paths that directly affect the queried entity (filtered subset of changed\_file\_paths). When querying changes for a specific entity (e.g., dbt model "customers"), this contains only the files that directly impact that entity (e.g., "models/customers.sql", "models/staging/stg\_customers.sql"). Empty when retrieving a change by change\_id directly (not scoped to a specific entity). Empty when it's impossible to determine entity-specific file mappings. Example: If a commit changes 50 files but only 2 affect your queried dbt model, this contains those 2 files. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_branch\_name.branch\_name | [optional string](#string) | Name of the branch associated with the commit, if any. |
| clone\_url | [ string](#string) | URL of the Git repository where the commit was made. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_web\_commit\_url.web\_commit\_url | [optional string](#string) | URL to e.g. Github which opens the Commit |
| directory\_changes | [repeated DirectoryChangeSummary](#directorychangesummary) | Hierarchical directory summaries showing where changes occurred. |
| changes\_by\_file\_type | [map ChangesByFileTypeEntry](#changesbyfiletypeentry) | Aggregated statistics by file type/extension. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_statistics.statistics | [optional ChangeStatistics](#changestatistics) | Overall statistical summary of the commit. |
### GitCommitChange.ChangeStatistics
(synq.entities.v1.GitCommitChange.ChangeStatistics)
ChangeStatistics provides statistical summaries of the entire commit.
| Field | Type | Description |
| - | - | - |
| total\_files | [ int32](#int32) | Total number of files changed. |
| total\_lines\_added | [ int32](#int32) | Total lines added across all files. |
| total\_lines\_removed | [ int32](#int32) | Total lines removed across all files. |
| distribution | [ FileChangeDistribution](#filechangedistribution) | Distribution of changes by magnitude. |
| top\_changes | [repeated TopChange](#topchange) | Top 10 files by total lines changed. |
### GitCommitChange.ChangeStats
(synq.entities.v1.GitCommitChange.ChangeStats)
ChangeStats contains statistics about changes made to a single file.
| Field | Type | Description |
| - | - | - |
| additions | [ int32](#int32) | Number of lines added to the file. |
| deletions | [ int32](#int32) | Number of lines removed from the file. |
### GitCommitChange.ChangesByFileTypeEntry
(synq.entities.v1.GitCommitChange.ChangesByFileTypeEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ FileTypeChangeSummary](#filetypechangesummary) | none |
### GitCommitChange.DirectoryChangeSummary
(synq.entities.v1.GitCommitChange.DirectoryChangeSummary)
DirectoryChangeSummary provides hierarchical directory-level statistics.
| Field | Type | Description |
| - | - | - |
| directory\_path | [ string](#string) | Path of the directory. |
| files\_changed | [ int32](#int32) | Number of files changed in this directory (excluding subdirectories). |
| total\_lines\_added | [ int32](#int32) | Total lines added in this directory (excluding subdirectories). |
| total\_lines\_removed | [ int32](#int32) | Total lines removed in this directory (excluding subdirectories). |
| subdirs | [repeated DirectoryChangeSummary](#directorychangesummary) | Subdirectory summaries. |
| top\_files | [repeated TopFileChange](#topfilechange) | Top 3-5 most significant file changes in this directory. |
### GitCommitChange.FileChangeDistribution
(synq.entities.v1.GitCommitChange.FileChangeDistribution)
FileChangeDistribution categorizes files by change magnitude.
| Field | Type | Description |
| - | - | - |
| files\_with\_small\_changes | [ int32](#int32) | Files with fewer than 10 lines changed. |
| files\_with\_medium\_changes | [ int32](#int32) | Files with 10-100 lines changed. |
| files\_with\_large\_changes | [ int32](#int32) | Files with more than 100 lines changed. |
| files\_added | [ int32](#int32) | Files that were newly added. |
| files\_modified | [ int32](#int32) | Files that were modified. |
| files\_deleted | [ int32](#int32) | Files that were deleted. |
### GitCommitChange.FileChangeStatsEntry
(synq.entities.v1.GitCommitChange.FileChangeStatsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ChangeStats](#changestats) | none |
### GitCommitChange.FileTypeChangeSummary
(synq.entities.v1.GitCommitChange.FileTypeChangeSummary)
FileTypeChangeSummary aggregates changes by file type/extension.
| Field | Type | Description |
| - | - | - |
| file\_extension | [ string](#string) | File extension (e.g., ".sql", ".py", ".yml"). |
| files\_count | [ int32](#int32) | Number of files with this extension that were changed. |
| total\_lines\_added | [ int32](#int32) | Total lines added across all files of this type. |
| total\_lines\_removed | [ int32](#int32) | Total lines removed across all files of this type. |
| example\_files | [repeated string](#string) | Example file paths (up to 5) showing where these changes occurred. |
| common\_directories | [repeated string](#string) | Common directories where files of this type were modified. |
### GitCommitChange.Signature
(synq.entities.v1.GitCommitChange.Signature)
Signature contains information about the Git user who made the changes.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the Git user. |
| email | [ string](#string) | Email address of the Git user. |
| created\_at | [ Timestamp](#timestamp) | Timestamp when the signature was created. |
### GitCommitChange.TopChange
(synq.entities.v1.GitCommitChange.TopChange)
TopChange represents one of the most significant file changes.
| Field | Type | Description |
| - | - | - |
| file\_path | [ string](#string) | Path of the file. |
| lines\_added | [ int32](#int32) | Lines added. |
| lines\_removed | [ int32](#int32) | Lines removed. |
| total\_lines\_changed | [ int32](#int32) | Total lines changed (added + removed). |
### GitCommitChange.TopFileChange
(synq.entities.v1.GitCommitChange.TopFileChange)
TopFileChange represents a significant file change within a directory.
| Field | Type | Description |
| - | - | - |
| file\_path | [ string](#string) | Path of the file relative to the directory. |
| lines\_added | [ int32](#int32) | Number of lines added. |
| lines\_removed | [ int32](#int32) | Number of lines removed. |
### SchemaChange
(synq.entities.v1.SchemaChange)
SchemaChange represents a change detected in a database schema.
| Field | Type | Description |
| - | - | - |
| column\_changes | [repeated SchemaColumnChange](#schemacolumnchange) | List of changes made to individual columns in the schema. |
### SchemaColumnChange
(synq.entities.v1.SchemaColumnChange)
SchemaColumnChange represents a specific change to a column in the schema.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) column\_change.column\_addition | [ ColumnAdditionChangeType](#columnadditionchangetype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) column\_change.column\_native\_type | [ ColumnNativeTypeChangeType](#columnnativetypechangetype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) column\_change.column\_removal | [ ColumnRemovalChangeType](#columnremovalchangetype) | none |
### SchemaColumnChange.ColumnAdditionChangeType
(synq.entities.v1.SchemaColumnChange.ColumnAdditionChangeType)
ColumnAdditionChangeType represents the addition of a new column to the schema.
| Field | Type | Description |
| - | - | - |
| column\_id | [ string](#string) | Identifier of the new column. See ColumnRemovalChangeType.column\_id. |
| native\_type | [ string](#string) | Data type of the new column. |
| ordinal\_position | [ int32](#int32) | Position where the column was added in the schema. |
| human\_name | [ string](#string) | Name of the column as it appears in the source system. See ColumnRemovalChangeType.human\_name. |
### SchemaColumnChange.ColumnNativeTypeChangeType
(synq.entities.v1.SchemaColumnChange.ColumnNativeTypeChangeType)
ColumnNativeTypeChangeType represents a change in a column's data type.
| Field | Type | Description |
| - | - | - |
| column\_id | [ string](#string) | Identifier of the modified column. See ColumnRemovalChangeType.column\_id. |
| previous\_native\_type | [ string](#string) | Previous data type of the column. |
| current\_native\_type | [ string](#string) | New data type of the column. |
| human\_name | [ string](#string) | Name of the column as it appears in the source system. See ColumnRemovalChangeType.human\_name. |
| ordinal\_position | [ int32](#int32) | Position of the changed column in the current version of the schema. |
### SchemaColumnChange.ColumnRemovalChangeType
(synq.entities.v1.SchemaColumnChange.ColumnRemovalChangeType)
ColumnRemovalChangeType represents the removal of a column from the schema.
| Field | Type | Description |
| - | - | - |
| column\_id | [ string](#string) | Identifier of the removed column. Normalized to lower case with special characters stripped, so several columns of one schema can share it. |
| native\_type | [ string](#string) | Native data type of the removed column. |
| ordinal\_position | [ int32](#int32) | Original position of the column in the schema. |
| human\_name | [ string](#string) | Name of the column as it appears in the source system, which is what distinguishes two columns sharing a column\_id. Empty when the source system reported no distinct name, and on changes recorded before this field existed. |
### SqlDefinitionChange
(synq.entities.v1.SqlDefinitionChange)
SqlDefinitionChange represents a change detected in a SQL definition.
| Field | Type | Description |
| - | - | - |
| previous\_sql | [ string](#string) | Previous version of the SQL definition. |
| previous\_sql\_state\_at | [ Timestamp](#timestamp) | Timestamp when the previous SQL state was valid. |
| current\_sql | [ string](#string) | Current version of the SQL definition. |
| current\_sql\_state\_at | [ Timestamp](#timestamp) | Timestamp when the current SQL state became valid. |
| sql\_diff | [ string](#string) | Diff showing the changes between previous and current SQL. |
### ForeignKeyReference
(synq.entities.v1.ForeignKeyReference)
ForeignKeyReference describes the target of a foreign key constraint.
| Field | Type | Description |
| - | - | - |
| database | [ string](#string) | Target database name |
| schema | [ string](#string) | Target schema name |
| table | [ string](#string) | Target table name |
| columns | [repeated string](#string) | Target column names in the referenced table |
### TableConstraint
(synq.entities.v1.TableConstraint)
TableConstraint represents a single constraint or index on a database table.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the constraint or index |
| type | [ TableConstraintType](#tableconstrainttype) | Type of the constraint (best-effort mapping to enum) |
| columns | [repeated string](#string) | Column names involved in this constraint or index |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_foreign\_key\_reference.foreign\_key\_reference | [optional ForeignKeyReference](#foreignkeyreference) | Foreign key reference details (only set when type is FOREIGN\_KEY) |
| index\_type | [ string](#string) | Index type (e.g. "btree", "hash", "gin", "gist") - only relevant for INDEX type |
| expression | [ string](#string) | Expression for check constraints or expression-based indexes |
| is\_unique | [ bool](#bool) | Whether this constraint or index enforces uniqueness |
| raw\_type | [ string](#string) | Raw constraint type string from the data warehouse (e.g. "PRIMARY KEY", "SORTING KEY", "PARTITION BY", "CLUSTER BY", "DISTRIBUTION KEY", "PROJECTION", "INDEX", "UNIQUE INDEX"). Preserved verbatim for consumers that need platform-specific details beyond the enum. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_enforced.is\_enforced | [optional bool](#bool) | Whether this constraint is enforced by the database. nil/unset = unknown, true = enforced, false = not enforced (e.g. Snowflake NORELY, Oracle DISABLED). |
### ImpactedDataproduct
(synq.entities.impact.v1.ImpactedDataproduct)
| Field | Type | Description |
| - | - | - |
| id | [ DataproductIdentifier](#dataproductidentifier) | none |
| title | [ string](#string) | none |
| description | [ string](#string) | none |
| folder | [ string](#string) | none |
| priority | [ Priority](#priority) | none |
| impacted\_entities | [repeated ImpactedEntityGroup](#impactedentitygroup) | none |
### ImpactedEntityGroup
(synq.entities.impact.v1.ImpactedEntityGroup)
Multiple Entities which form logical group, e.g. dbt Model and it's table
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_name.name | [optional string](#string) | none |
| ids | [repeated Identifier](#identifier) | none |
| synq\_paths | [repeated string](#string) | none |
### ImpactedOwner
(synq.entities.impact.v1.ImpactedOwner)
| Field | Type | Description |
| - | - | - |
| id | [ OwnerIdentifier](#owneridentifier) | none |
| title | [ string](#string) | none |
| impacted\_entities | [repeated ImpactedEntityGroup](#impactedentitygroup) | none |
### GetImpactRequest
(synq.entities.impact.v1.GetImpactRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | none |
### GetImpactResponse
(synq.entities.impact.v1.GetImpactResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated ImpactedEntityGroup](#impactedentitygroup) | none |
| downstream\_entities | [repeated ImpactedEntityGroup](#impactedentitygroup) | none |
| dataproducts | [repeated ImpactedDataproduct](#impacteddataproduct) | none |
| downstream\_dataproducts | [repeated ImpactedDataproduct](#impacteddataproduct) | none |
| owners | [repeated ImpactedOwner](#impactedowner) | none |
| downstream\_owners | [repeated ImpactedOwner](#impactedowner) | none |
### DatabaseCoordinates
(synq.entities.coordinates.v1.DatabaseCoordinates)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
| synq\_paths | [repeated string](#string) | none |
| dialect\_name | [ string](#string) | none |
| dialect | [ SqlDialect](#sqldialect) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_data\_platform.data\_platform | [optional DataPlatformIdentifier](#dataplatformidentifier) | none |
| data\_platform\_type | [ DataPlatformType](#dataplatformtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_connection\_id.connection\_id | [optional string](#string) | none |
| instance\_name | [ string](#string) | none |
| database\_name | [ string](#string) | none |
| schema\_name | [ string](#string) | none |
| object\_name | [ string](#string) | none |
| sql\_fqn | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sql\_database\_name.sql\_database\_name | [optional string](#string) | SQL-addressable components (only parts usable in SQL queries). For Postgres, sql\_database\_name is empty since cross-database queries are not supported. |
| sql\_schema\_name | [ string](#string) | none |
| sql\_object\_name | [ string](#string) | none |
### DatabaseContext
(synq.entities.coordinates.v1.DatabaseContext)
DatabaseContext provides the default database execution context
for resolving unqualified references (e.g. table names in SQL).
| Field | Type | Description |
| - | - | - |
| instance\_name | [ string](#string) | Instance identifier (e.g. Snowflake account, Databricks workspace URL, BigQuery project) |
| database\_name | [ string](#string) | Database or catalog name |
| schema\_name | [ string](#string) | Schema name |
| object\_name | [ string](#string) | Object name (e.g. table or view name) that the SQL writes to or creates. Only set when the SQL defines or materializes an object, e.g. `CREATE TABLE AS SELECT ...`. Leave empty for read-only SQL such as dashboard queries or SELECT statements. |
### BatchDatabaseCoordinatesRequest
(synq.entities.coordinates.v1.BatchDatabaseCoordinatesRequest)
BatchGetEntitiesRequest is the request message for the BatchGetEntities method.
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | Identifiers of the entities to get. |
### BatchDatabaseCoordinatesResponse
(synq.entities.coordinates.v1.BatchDatabaseCoordinatesResponse)
BatchGetEntitiesResponse is the response message for the BatchGetEntities method.
| Field | Type | Description |
| - | - | - |
| coordinates | [repeated DatabaseCoordinates](#databasecoordinates) | The entities that were retrieved. |
### BatchIdsByCoordinatesRequest
(synq.entities.coordinates.v1.BatchIdsByCoordinatesRequest)
| Field | Type | Description |
| - | - | - |
| sql\_fqn | [repeated string](#string) | Databases coordinates as present in the SQL, e.g. `db.schema.table` |
### BatchIdsByCoordinatesResponse
(synq.entities.coordinates.v1.BatchIdsByCoordinatesResponse)
| Field | Type | Description |
| - | - | - |
| matched\_coordinates | [repeated MatchedCoordinates](#matchedcoordinates) | none |
### BatchIdsByCoordinatesResponse.MatchedCoordinates
(synq.entities.coordinates.v1.BatchIdsByCoordinatesResponse.MatchedCoordinates)
| Field | Type | Description |
| - | - | - |
| sql\_fqn | [ string](#string) | none |
| candidates | [repeated DatabaseCoordinates](#databasecoordinates) | none |
### ActivitySelection
(synq.entities.activity.v1alpha.ActivitySelection)
ActivitySelection picks the entities a read is about: a data product, an owner, a saved filter or
any other query. A data product is `in_product(...)` in resolver\_ql, an owner `in_owner(...)`.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.resolver\_ql | [ string](#string) | The entities in resolver query language, the fuller of the two forms. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.public\_query | [ Query](#query) | The entities in structured form, as the filter builder produces it. |
### ActorGroupRef
(synq.entities.activity.v1alpha.ActorGroupRef)
ActorGroupRef names one group of GetActivityOverview. Its summary has exactly the group's queries, so
it can be narrower than the actor of the same name: a login a service account holds can land with
another account first, and Coalesce Quality's queries land in the COALESCE group.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) group.service\_account\_id | [ string](#string) | The SERVICE\_ACCOUNT group of this service account, a built-in one included. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) group.issuer | [ DataPlatformType](#dataplatformtype) | The TOOL group of this tool. |
| built\_in | [ bool](#bool) | Output only: service\_account\_id names a built-in account. |
### ActorRef
(synq.entities.activity.v1alpha.ActorRef)
ActorRef names who an actor summary is about.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.platform\_user\_id | [ string](#string) | One login, on every tool that issued its queries. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.issuer\_entity\_id | [ string](#string) | A job, task or step that issues queries or runs what builds an entity: what it maintains. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.issuer | [ DataPlatformType](#dataplatformtype) | A tool, from the tag its queries carry. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.service\_account\_id | [ string](#string) | The logins a service account holds, a built-in one included, such as `built_in.fivetran`. `built_in.coalesce-quality` is the same actor as coalesce. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.person\_identity | [ string](#string) | The logins linked to this person, such as "synq:\" or "email:\". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.coalesce | [ bool](#bool) | Coalesce Quality and Coalesce Catalog: every query that carried Coalesce Quality's tag, on any login, and every query of a login either connects as. What the overview's COALESCE group counts. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.group | [ ActorGroupRef](#actorgroupref) | One group of GetActivityOverview, exactly the logins the overview lands in it: what ActorGroup.actor names for a service account or a tool. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.person\_identities | [ PersonIdentities](#personidentities) | One person through every identity of theirs, each login counted once. |
### GridDay
(synq.entities.activity.v1alpha.GridDay)
GridDay is one weekday of a weekday and hour grid.
| Field | Type | Description |
| - | - | - |
| iso\_weekday | [ uint32](#uint32) | 1 is Monday, 7 is Sunday. |
| hours | [repeated uint64](#uint64) | 24 counts, one per local hour. |
| occurrences | [ uint32](#uint32) | How many local dates with this weekday overlap the window, to average by. |
### IssuerCount
(synq.entities.activity.v1alpha.IssuerCount)
IssuerCount is one issuing tool's part of a point.
| Field | Type | Description |
| - | - | - |
| issuer | [ DataPlatformType](#dataplatformtype) | DATA\_PLATFORM\_TYPE\_UNSPECIFIED is access whose query carried no tool's tag. |
| count | [ uint64](#uint64) | none |
### NotCountedActivity
(synq.entities.activity.v1alpha.NotCountedActivity)
NotCountedActivity is access a read saw and left out.
| Field | Type | Description |
| - | - | - |
| coalesce\_logins | [ uint64](#uint64) | Access by the logins Coalesce Quality connects as, on queries that carried no Coalesce Quality tag. A tagged query is in coalesce\_quality\_queries instead. |
| writers\_own\_reads | [ uint64](#uint64) | Reads by a login of an entity it wrote the same UTC day. |
| intermediate | [ uint64](#uint64) | Reads in an hour the entity was an intermediate table. |
| coalesce\_quality\_queries | [ uint64](#uint64) | Coalesce Quality's own queries, on any login, such as a monitor or a SQL test reading the entity. Never counted. |
| excluded\_by\_policy | [ uint64](#uint64) | Reads by logins whose service account does not count toward popularity, such as a loader or an orchestrator. |
| excluded\_by\_lens | [ uint64](#uint64) | Access the asked lens does not look at, such as a BI tool's reads under DEVELOPMENT. |
### PersonIdentities
(synq.entities.activity.v1alpha.PersonIdentities)
PersonIdentities names one person by the identities of theirs a caller holds: what
synq.users.v1.UsersService.GetUserIdentities returns for them, such as "synq:\", their
"email:\" and the "platform\_user:\" logins linked to them, and the BI viewers those
resolve. Only a login that is somebody's own, or nobody's, is the person's: a login a service
account holds never is.
| Field | Type | Description |
| - | - | - |
| identities | [repeated string](#string) | none |
### PlatformShares
(synq.entities.activity.v1alpha.PlatformShares)
PlatformShares are the shares of the queries on one platform.
| Field | Type | Description |
| - | - | - |
| platform | [ DataPlatformType](#dataplatformtype) | none |
| compute\_unit | [ ComputeUnit](#computeunit) | none |
| shares | [ QueryShares](#queryshares) | none |
### PlatformWeights
(synq.entities.activity.v1alpha.PlatformWeights)
PlatformWeights are the weights of the queries on one platform.
| Field | Type | Description |
| - | - | - |
| platform | [ DataPlatformType](#dataplatformtype) | DATA\_PLATFORM\_TYPE\_UNSPECIFIED is the queries whose platform is not known. |
| compute\_unit | [ ComputeUnit](#computeunit) | What weights.compute\_ms counts. |
| weights | [ QueryWeights](#queryweights) | none |
### PoolRank
(synq.entities.activity.v1alpha.PoolRank)
PoolRank is an entity's place in one pool.
| Field | Type | Description |
| - | - | - |
| pool | [ PopularityPool](#popularitypool) | none |
| platform | [ DataPlatformType](#dataplatformtype) | The platform and instance the pool is on, as far as the pool names them. |
| instance | [ string](#string) | none |
| reads | [ uint64](#uint64) | The entity's reads in this pool. |
| percentile | [ double](#double) | The share of the pool's other entities with fewer reads, from 0 to 1. Entities with equal reads share the lower place, and the only entity of a pool has 1. |
| pool\_size | [ uint32](#uint32) | The entities in the pool: those with counted reads in the window. |
### QueryShares
(synq.entities.activity.v1alpha.QueryShares)
QueryShares are the weights of each query split equally over the entities it touched, so they add
up across entities. Equal is a convention, as no platform reports cost per table. An unset figure
is one no query reported.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_ms.execution\_ms | [optional double](#double) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_compute\_ms.compute\_ms | [optional double](#double) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bytes\_scanned.bytes\_scanned | [optional double](#double) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bytes\_billed.bytes\_billed | [optional double](#double) | none |
### QueryWeights
(synq.entities.activity.v1alpha.QueryWeights)
QueryWeights are whole weights summed over queries. An unset figure is one no query reported, and
zero is a value. Nothing here is a credit or a price.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_ms.execution\_ms | [optional uint64](#uint64) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_compute\_ms.compute\_ms | [optional uint64](#uint64) | In the unit of the platform the weights are for. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bytes\_scanned.bytes\_scanned | [optional uint64](#uint64) | What the queries scanned. Never a cost: a platform can bill differently from what it scanned. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_bytes\_billed.bytes\_billed | [optional uint64](#uint64) | Set only where the platform bills by bytes read. |
### SeriesPoint
(synq.entities.activity.v1alpha.SeriesPoint)
SeriesPoint is one step of a series. A series is dense: a step with nothing counted is a point with
count 0, and covered or covered\_hours say whether it was seen at all.
| Field | Type | Description |
| - | - | - |
| start | [ Timestamp](#timestamp) | none |
| count | [ uint64](#uint64) | none |
| by\_issuer | [repeated IssuerCount](#issuercount) | With SERIES\_SPLIT\_ISSUER only: each tool's count, highest first, then by issuer. |
| covered | [ bool](#bool) | On an hourly point: whether the hour's query history was read. |
| covered\_hours | [ uint32](#uint32) | On a daily point: how many of the day's hours were read. A day has 23 or 25 hours when the time zone changes its offset. |
| partial | [ bool](#bool) | On a daily point: the day is not wholly inside the window. |
### WeekGrid
(synq.entities.activity.v1alpha.WeekGrid)
WeekGrid is when access happens in the week: seven days, Monday first.
| Field | Type | Description |
| - | - | - |
| days | [repeated GridDay](#gridday) | none |
### ActorActivity
(synq.entities.activity.v1alpha.ActorActivity)
ActorActivity is one thing that touched an entity in one direction and measure, with its count.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.issuer\_entity\_id | [ string](#string) | A job, task or step, such as "dbt model M writes this". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.service\_account\_id | [ string](#string) | A service account, a built-in one included. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.issuer | [ DataPlatformType](#dataplatformtype) | A tool, from the tag on queries no job is named for, such as "Looker reads this". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.platform\_user\_id | [ string](#string) | none |
| count | [ uint64](#uint64) | none |
| logins | [ uint32](#uint32) | The distinct logins behind the count. 1 for a login. |
| last\_seen\_at | [ Timestamp](#timestamp) | none |
| basis | [ LoginBasis](#loginbasis) | For a login only. |
| person\_identity | [ string](#string) | For a login with basis OWN\_LOGIN, VIEWER\_EMAIL or VIEWER\_TOOL\_USER: the identity of its person, such as "email:\", empty when none resolves. A member whose identities hold the login's "platform\_user:\" wins over it: that is a platform user alias, and the caller applies it. |
| active\_days | [ uint32](#uint32) | The distinct UTC days with counted access. |
| shares | [ QueryShares](#queryshares) | The summed shares of the queries behind the count. |
| purpose | [ Purpose](#purpose) | What it acts for: a service account's purpose, else the purpose of the tool that issued its queries. Unspecified for a login, and for a tool with no known purpose. |
| cadence | [ WriteCadence](#writecadence) | How often it accessed the entity in the window. |
| built\_in | [ bool](#bool) | Set when service\_account\_id names a built-in account. |
| entity\_id | [ string](#string) | For a service account, its entity id, which resolves to its name like any other entity. |
### ActorComputeTotals
(synq.entities.activity.v1alpha.ActorComputeTotals)
ActorComputeTotals is an actor's queries on one compute of one platform instance.
| Field | Type | Description |
| - | - | - |
| platform\_root\_id | [ string](#string) | none |
| compute\_id | [ string](#string) | none |
| platform | [ DataPlatformType](#dataplatformtype) | DATA\_PLATFORM\_TYPE\_UNSPECIFIED when the instance's platform is not known. |
| compute\_unit | [ ComputeUnit](#computeunit) | none |
| queries | [ uint64](#uint64) | none |
| weights | [ QueryWeights](#queryweights) | none |
### ActorEntityActivity
(synq.entities.activity.v1alpha.ActorEntityActivity)
ActorEntityActivity is what an actor did to one entity in one direction and measure.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| direction | [ ActivityDirection](#activitydirection) | none |
| measure | [ ActivityMeasure](#activitymeasure) | none |
| count | [ uint64](#uint64) | none |
| shares | [ QueryShares](#queryshares) | none |
| last\_seen\_at | [ Timestamp](#timestamp) | none |
| active\_days | [ uint32](#uint32) | none |
### ActorFileOperation
(synq.entities.activity.v1alpha.ActorFileOperation)
ActorFileOperation is an actor's queries of one file operation on one location.
| Field | Type | Description |
| - | - | - |
| operation | [ FileOperation](#fileoperation) | none |
| location\_entity\_id | [ string](#string) | The stage or location the files moved through, empty when none is known. A temporary stage a tool makes per run is one location. |
| queries | [ uint64](#uint64) | none |
| weights | [repeated PlatformWeights](#platformweights) | The whole weights of the queries per platform, by platform. |
### ActorGroup
(synq.entities.activity.v1alpha.ActorGroup)
ActorGroup is the queries of the logins that land in one group.
| Field | Type | Description |
| - | - | - |
| kind | [ ActorGroupKind](#actorgroupkind) | none |
| actor | [ ActorRef](#actorref) | The actor to ask GetActorSummary about, whose summary has the same queries, compute and logins as the group. Unset for PEOPLE, LOGINS and UNATTRIBUTED. |
| logins | [ uint32](#uint32) | The distinct logins behind the queries. |
| queries | [ uint64](#uint64) | none |
| weights | [repeated PlatformWeights](#platformweights) | The group's weights per platform, by platform. |
| daily | [repeated SeriesPoint](#seriespoint) | The queries per day in the asked time zone, and when in the week. Under a selection, the reads, inputs and writes of the selected entities. |
| grid | [ WeekGrid](#weekgrid) | none |
| reads | [ uint64](#uint64) | The group's access to entities, per entity a query touched, so a query that read three tables is three reads. Under a selection, of the selected entities only. Only with include\_flows or a selection. |
| inputs | [ uint64](#uint64) | none |
| writes | [ uint64](#uint64) | none |
| entities\_read | [ uint32](#uint32) | The entities it read or input, and wrote, entities that are the same counted once. |
| entities\_written | [ uint32](#uint32) | none |
| shares | [repeated PlatformShares](#platformshares) | Under a selection: the group's share of its queries' weights on the selected entities, by platform. Unset without one, where weights is the whole. |
### ActorIssuerEntity
(synq.entities.activity.v1alpha.ActorIssuerEntity)
ActorIssuerEntity is one job behind an actor's queries: the queries it issued and what it wrote.
| Field | Type | Description |
| - | - | - |
| issuer\_entity\_id | [ string](#string) | none |
| issuer | [ DataPlatformType](#dataplatformtype) | The tool that issued its queries. |
| queries | [ uint64](#uint64) | none |
| weights | [repeated PlatformWeights](#platformweights) | The whole weights of its queries per platform, by platform. |
| writes | [ uint64](#uint64) | Its writes, one per entity each write query touched, and the entities it wrote, entities that are the same counted once. |
| entities\_written | [ uint32](#uint32) | none |
### ActorTotals
(synq.entities.activity.v1alpha.ActorTotals)
ActorTotals is an actor's whole window.
| Field | Type | Description |
| - | - | - |
| queries | [ uint64](#uint64) | Its queries, whatever they touched. |
| weights | [repeated PlatformWeights](#platformweights) | The whole weights of its queries, per platform, by platform. |
| reads | [ uint64](#uint64) | Access to entities, per direction. |
| inputs | [ uint64](#uint64) | none |
| writes | [ uint64](#uint64) | Write queries. Runs are never part of it. |
| runs | [ uint64](#uint64) | The runs of what a job builds. |
| entities\_read | [ uint32](#uint32) | The distinct entities it read, and wrote or built. |
| entities\_written | [ uint32](#uint32) | none |
| active\_days | [ uint32](#uint32) | The distinct UTC days with any access. |
| outside\_entities | [ QueryShares](#queryshares) | The whole weight less the entity shares, never below 0: what the queries spent outside entities, such as on a pipeline's staging tables. Each figure is a sum across platforms. |
### ActorWrittenSchema
(synq.entities.activity.v1alpha.ActorWrittenSchema)
ActorWrittenSchema is what an actor wrote in one schema.
| Field | Type | Description |
| - | - | - |
| schema | [ WarehouseSchema](#warehouseschema) | none |
| entities\_written | [ uint32](#uint32) | The entities it wrote there, entities that are the same counted once each. |
| writes | [ uint64](#uint64) | none |
| cadence | [ WriteCadence](#writecadence) | How often it wrote there in the window. |
### BatchGetEntityUsageRequest
(synq.entities.activity.v1alpha.BatchGetEntityUsageRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | none |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| top\_k | [ uint32](#uint32) | At most this many roles of each kind, such as the three tools that read the entity most. Unset is 3. |
### BatchGetEntityUsageResponse
(synq.entities.activity.v1alpha.BatchGetEntityUsageResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated EntityUsage](#entityusage) | One per distinct asked id, in the order each was first asked. |
| from | [ Timestamp](#timestamp) | The window read, \[from, to). |
| to | [ Timestamp](#timestamp) | none |
### BatchGetPopularityRequest
(synq.entities.activity.v1alpha.BatchGetPopularityRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | none |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour, so the window holds closed hours only. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| pool | [ PopularityPool](#popularitypool) | The pools to rank in. Unset is SYSTEM. |
| lens | [ ActivityLens](#activitylens) | Which access counts, in the counts and the ranks alike. Unset is CONSUMPTION. Under DEVELOPMENT a measure's reads hold what was read to build it too. |
### BatchGetPopularityResponse
(synq.entities.activity.v1alpha.BatchGetPopularityResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated EntityPopularity](#entitypopularity) | One per distinct asked id, in the order each was first asked. |
| from | [ Timestamp](#timestamp) | The window read, \[from, to). |
| to | [ Timestamp](#timestamp) | none |
| covered\_hours | [ uint32](#uint32) | Hours of the window whose query history was read. An hour that was not read counts nothing either way. |
| ranks\_unavailable | [ bool](#bool) | Set when the ranks could not be computed. The counts are still served, and every rank list is empty. |
| ranks\_pending | [ bool](#bool) | Set when the ranks of the default window are not ready for every asked entity yet. The counts are served, and an entity has no ranks until they are. |
| ranks\_window\_to | [ Timestamp](#timestamp) | The end of the window the ranks were computed for, exclusive, when it differs from to: the ranks of the default window are computed once a day, for the window ending at 00:00 UTC. |
### CurrentReading
(synq.entities.activity.v1alpha.CurrentReading)
CurrentReading is a platform's own current figure for an entity, such as "40 views in the last 28
days". Compare it only with readings of the same shape and window.
| Field | Type | Description |
| - | - | - |
| shape | [ ActivityShape](#activityshape) | TRAILING\_WINDOW or LATEST\_VALUE. |
| window\_days | [ uint32](#uint32) | M of "N in the last M days". Zero for a LATEST\_VALUE reading. |
| count | [ uint64](#uint64) | N. |
| observed\_at | [ Timestamp](#timestamp) | When the figure was read from the platform. |
| direction | [ ActivityDirection](#activitydirection) | none |
### DirectionActors
(synq.entities.activity.v1alpha.DirectionActors)
| Field | Type | Description |
| - | - | - |
| direction | [ ActivityDirection](#activitydirection) | none |
| measure | [ ActivityMeasure](#activitymeasure) | none |
| total | [ uint64](#uint64) | The direction's count: every counted access, whoever it was. The lists below overlap, and a login can be left off one by its policy, so they do not add up to it. |
| issuer\_entities | [repeated ActorActivity](#actoractivity) | none |
| service\_accounts | [repeated ActorActivity](#actoractivity) | The workspace's service accounts first, then the built-in ones, each by count. |
| tools | [repeated ActorActivity](#actoractivity) | none |
| logins | [repeated ActorActivity](#actoractivity) | Every login, whatever issued its queries, except those whose policy keeps them off the frequent users list. A login known two ways is listed once per basis. |
| unattributed | [ uint64](#uint64) | The count no login is named for. |
### DirectionSeries
(synq.entities.activity.v1alpha.DirectionSeries)
DirectionSeries is one direction's access over the window.
| Field | Type | Description |
| - | - | - |
| direction | [ ActivityDirection](#activitydirection) | none |
| measure | [ ActivityMeasure](#activitymeasure) | none |
| total | [ uint64](#uint64) | none |
| hourly | [repeated SeriesPoint](#seriespoint) | One point per UTC hour of the window. Empty for a measure platforms only report per day, such as VIEWS. |
| daily | [repeated SeriesPoint](#seriespoint) | One point per day the window overlaps, in the asked time zone. |
| grid | [ WeekGrid](#weekgrid) | Unset for a measure reported only per day. |
### EntityActors
(synq.entities.activity.v1alpha.EntityActors)
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| actors | [repeated DirectionActors](#directionactors) | One per direction and measure with counted access: READ, INPUT, WRITE, then in measure order. INPUT is counted only under the DEVELOPMENT lens. |
| not\_counted | [ NotCountedActivity](#notcountedactivity) | none |
### EntityPopularity
(synq.entities.activity.v1alpha.EntityPopularity)
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| measures | [repeated MeasurePopularity](#measurepopularity) | One per measure with counted reads or writes, or with a current reading, in measure order. |
| not\_counted | [ NotCountedActivity](#notcountedactivity) | Access the read saw and did not count. |
### EntityUsage
(synq.entities.activity.v1alpha.EntityUsage)
EntityUsage is how one entity is used. An entity is answered with the entities it is the same as,
such as a dbt model and the table it builds, and with the other side of a share, so asking for
the model or for its table gives the same roles.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| roles | [repeated UsageRole](#usagerole) | In role order, then by count, highest first. |
| covered\_hours | [ uint32](#uint32) | Hours of the window whose query history was read. An hour that was not read counts nothing. |
| window\_hours | [ uint32](#uint32) | The hours of the window. |
### GetActivityOverviewRequest
(synq.entities.activity.v1alpha.GetActivityOverviewRequest)
| Field | Type | Description |
| - | - | - |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| time\_zone | [ string](#string) | The IANA time zone days and grid cells are in. Unset is UTC. |
| pool | [ PopularityPool](#popularitypool) | The pool most\_read is ranked in. Unset is SYSTEM. |
| measure | [ ActivityMeasure](#activitymeasure) | The measure most\_read reads. Unset is QUERIES. |
| top\_k | [ uint32](#uint32) | At most this many most\_read rows. Unset is 20. |
| selection | [ ActivitySelection](#activityselection) | Only these entities, such as a data product's. Unset is the whole workspace. A selection of more than 50,000 entities, counting the entities each is the same as, is refused with INVALID\_ARGUMENT. |
Under a selection a group is its access to the selected entities, never its whole cost: its shares of its queries' weights, not the weights, so the groups add up to what the selection costs. A query that read three selected tables is three reads and its weight is counted once, so queries and weights are unset and reads, inputs and writes count the access. most\_read lists the selected entities, each with its rank in the whole pool. |
\| include\_flows | [ bool](#bool) | Also count each group's reads, inputs and writes and the entities it read and wrote, and the live and touched tables. Without a selection they read the access to every entity, which can take seconds on a large workspace, so a client shows the groups first and asks for these in a second call. A selection always counts them, as they are its groups. |
### GetActivityOverviewResponse
(synq.entities.activity.v1alpha.GetActivityOverviewResponse)
| Field | Type | Description |
| - | - | - |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
| time\_zone | [ string](#string) | The time zone used, "UTC" when none was asked. |
| covered\_hours | [ uint32](#uint32) | Hours of the window whose query history was read. |
| queries | [ uint64](#uint64) | Every query of the workspace, Coalesce Quality's included, as they cost compute too. |
| weights | [repeated PlatformWeights](#platformweights) | The workspace's weights per platform, by platform. |
| groups | [repeated ActorGroup](#actorgroup) | By compute, highest first, then queries. |
| most\_read | [repeated MostRead](#mostread) | The most read entities under CONSUMPTION, by reads. |
| entities\_read | [ uint32](#uint32) | The entities with counted reads under CONSUMPTION, entities that are the same counted once. |
| ranks\_unavailable | [ bool](#bool) | Set when the ranks could not be computed. The groups are still served, and most\_read is empty. |
| entities\_live | [ uint32](#uint32) | The live warehouse tables and views of the workspace or the selection, entities that are the same counted once, and those of them anyone but Coalesce Quality read, input or wrote in the window, through them or through a table a share shows in another account, so "2,530 of 7,196 tables in use". A table on a platform with no query history in the window counts in neither. Only with include\_flows or a selection. |
| entities\_touched | [ uint32](#uint32) | none |
| shares | [repeated PlatformShares](#platformshares) | Under a selection: the selected entities' share of each platform's weights, by platform. Unset without one, where weights is the whole. |
| flows\_included | [ bool](#bool) | Set when the groups' reads, inputs and writes and the live and touched tables were counted, with include\_flows or a selection. Unset, they read 0 because they were not asked for, and a client shows them as loading while it asks again with include\_flows. |
### GetActorSummaryRequest
(synq.entities.activity.v1alpha.GetActorSummaryRequest)
| Field | Type | Description |
| - | - | - |
| actor | [ ActorRef](#actorref) | none |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| time\_zone | [ string](#string) | The IANA time zone days and grid cells are in. Unset is UTC. |
| top\_k | [ uint32](#uint32) | At most this many entities per direction. Unset is 20. |
### GetActorSummaryResponse
(synq.entities.activity.v1alpha.GetActorSummaryResponse)
| Field | Type | Description |
| - | - | - |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
| time\_zone | [ string](#string) | The time zone used, "UTC" when none was asked. |
| platform\_user\_ids | [repeated string](#string) | The logins the actor resolved to, sorted. Empty without the platform users scope. |
| totals | [ ActorTotals](#actortotals) | none |
| computes | [repeated ActorComputeTotals](#actorcomputetotals) | Ordered by platform instance, then compute. |
| entities | [repeated ActorEntityActivity](#actorentityactivity) | At most top\_k per direction, READ, INPUT, then WRITE, each by count. For a job, a WRITE row of measure RUNS counts the runs of what it builds, never write queries. |
| hourly | [repeated SeriesPoint](#seriespoint) | The actor's queries per UTC hour, split by issuer. |
| daily | [repeated SeriesPoint](#seriespoint) | none |
| grid | [ WeekGrid](#weekgrid) | none |
| issuer\_entities | [repeated ActorIssuerEntity](#actorissuerentity) | The jobs behind the actor's queries, such as a DAG, a dbt model, a Streamlit app, or a monitor or SQL test of Coalesce Quality's: by queries, highest first, then writes. At most top\_k. |
| written\_schemas | [repeated ActorWrittenSchema](#actorwrittenschema) | What the actor wrote itself, per warehouse schema, by writes, highest first. Runs it orchestrated are not writes. The entities whose schema is not known are one entry with schema unset, last. |
| file\_operations | [repeated ActorFileOperation](#actorfileoperation) | The actor's queries that moved files, per operation and location, such as the files it put to a stage, by queries, highest first. |
### GetEntityActivitySeriesRequest
(synq.entities.activity.v1alpha.GetEntityActivitySeriesRequest)
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. 28 gives every weekday four times. |
| lens | [ ActivityLens](#activitylens) | none |
| measure | [ ActivityMeasure](#activitymeasure) | Unset is QUERIES. |
| time\_zone | [ string](#string) | The IANA time zone days and grid cells are in, such as "Europe/Warsaw". Unset is UTC. The window itself stays in whole UTC hours. |
| split | [ SeriesSplit](#seriessplit) | none |
### GetEntityActivitySeriesResponse
(synq.entities.activity.v1alpha.GetEntityActivitySeriesResponse)
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| from | [ Timestamp](#timestamp) | The window read, \[from, to). |
| to | [ Timestamp](#timestamp) | none |
| time\_zone | [ string](#string) | The time zone used, "UTC" when none was asked. |
| directions | [repeated DirectionSeries](#directionseries) | The lens's directions, then WRITE, each only when it has counted access. |
| not\_counted | [ NotCountedActivity](#notcountedactivity) | none |
### ListAccessSignalsRequest
(synq.entities.activity.v1alpha.ListAccessSignalsRequest)
| Field | Type | Description |
| - | - | - |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| selection | [ ActivitySelection](#activityselection) | Only these entities, such as a data product's. Unset is the whole workspace. A selection of more than 50,000 entities, counting the entities each is the same as, is refused with INVALID\_ARGUMENT. |
| signal | [ AccessSignal](#accesssignal) | none |
| pagination | [ Pagination](#pagination) | Without a page size every entity is returned. |
### ListAccessSignalsResponse
(synq.entities.activity.v1alpha.ListAccessSignalsResponse)
| Field | Type | Description |
| - | - | - |
| signal | [ AccessSignal](#accesssignal) | none |
| entities | [repeated SignalEntity](#signalentity) | One page of the entities that raise the signal. BUILT\_NEVER\_READ by build compute, highest first, READ\_WITHOUT\_VIEWER by shared reads and WRITTEN\_OUTSIDE\_PIPELINE by outside writes, each then by entity id. UNTOUCHED by schema, then entity id, those with no known schema last. |
| total | [ uint32](#uint32) | How many entities raise the signal, on every page. |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
| page\_info | [ PageInfo](#pageinfo) | none |
| schemas | [repeated SchemaSignal](#schemasignal) | UNTOUCHED: every schema with untouched tables and views, by how many, highest first. |
| tools | [repeated ToolSharedReads](#toolsharedreads) | READ\_WITHOUT\_VIEWER: the counted reads of every entity per tool, and those of them through a shared login, by shared reads, highest first. |
### ListActorsRequest
(synq.entities.activity.v1alpha.ListActorsRequest)
| Field | Type | Description |
| - | - | - |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| selection | [ ActivitySelection](#activityselection) | Only the access to these entities, such as a data product's. Unset is the whole workspace. Under a selection an actor is its access to the selected entities: its reads, inputs and writes and its shares of their cost, with no queries and no weights. A selection of more than 50,000 entities, counting the entities each is the same as, is refused with INVALID\_ARGUMENT. |
| kinds | [repeated ActorKind](#actorkind) | Only actors of these kinds. Empty is every kind the caller may see. |
| sort | [ ActorSort](#actorsort) | none |
| platform | [ DataPlatformType](#dataplatformtype) | The platform ACTOR\_SORT\_COMPUTE sorts on and compute\_fraction is of. Required with it. |
| pagination | [ Pagination](#pagination) | Without a page size every actor is returned. |
### ListActorsResponse
(synq.entities.activity.v1alpha.ListActorsResponse)
| Field | Type | Description |
| - | - | - |
| actors | [repeated ListedActor](#listedactor) | In the asked order. |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
| page\_info | [ PageInfo](#pageinfo) | none |
| people\_hidden | [ bool](#bool) | Set when the caller may not see people and logins: no PERSON or LOGIN actor is listed. |
### ListEntityActorsRequest
(synq.entities.activity.v1alpha.ListEntityActorsRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | none |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| top\_k | [ uint32](#uint32) | At most this many entries per list. Unset returns every entry. |
| lens | [ ActivityLens](#activitylens) | Which access counts. Unset is CONSUMPTION, under which each direction's total is what BatchGetPopularity counts. |
| directions | [repeated ActivityDirection](#activitydirection) | Only these directions. Empty is every direction the lens serves. |
| measures | [repeated ActivityMeasure](#activitymeasure) | Only these measures. Empty is every measure. |
### ListEntityActorsResponse
(synq.entities.activity.v1alpha.ListEntityActorsResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated EntityActors](#entityactors) | One per distinct asked id, in the order each was first asked. |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
| logins\_hidden | [ bool](#bool) | Set when the caller may not see logins: every logins list is empty. Every count is still served. |
### ListPersonActivityRequest
(synq.entities.activity.v1alpha.ListPersonActivityRequest)
| Field | Type | Description |
| - | - | - |
| identities | [repeated string](#string) | The identities of one person, as synq.users.v1 gives them, such as "synq:\", "email:\" or "platform\_user:\". |
| until | [ Timestamp](#timestamp) | The window ends at the start of this hour, exclusive. Unset is the current hour. |
| window\_days | [ uint32](#uint32) | The window's length in days, back from until. Unset is 30. |
| directions | [repeated ActivityDirection](#activitydirection) | Only these directions. Empty keeps every one. |
| pagination | [ Pagination](#pagination) | Without a page size every entity is returned. |
### ListPersonActivityResponse
(synq.entities.activity.v1alpha.ListPersonActivityResponse)
| Field | Type | Description |
| - | - | - |
| entities | [repeated PersonEntityActivity](#personentityactivity) | Ordered by count, highest first, then entity id, direction and measure. |
| platform\_user\_ids | [repeated string](#string) | The logins the identities resolved to, sorted. |
| from | [ Timestamp](#timestamp) | none |
| to | [ Timestamp](#timestamp) | none |
| page\_info | [ PageInfo](#pageinfo) | none |
### ListedActor
(synq.entities.activity.v1alpha.ListedActor)
ListedActor is one actor of ListActors over the window.
| Field | Type | Description |
| - | - | - |
| kind | [ ActorKind](#actorkind) | none |
| actor | [ ActorRef](#actorref) | The actor to ask GetActorSummary about. Without a selection its summary has the same totals. |
| built\_in | [ bool](#bool) | Set when actor names a built-in account. |
| entity\_id | [ string](#string) | For a service account, its entity id, which resolves to its name like any other entity. |
| logins | [ uint32](#uint32) | The distinct logins behind its queries and its access. |
| totals | [ ActorTotals](#actortotals) | As GetActorSummary has them. Under a selection, the access to the selected entities only: queries, weights, runs and outside\_entities are unset. |
| shares | [repeated PlatformShares](#platformshares) | Under a selection: its share of what the selected entities cost, by platform. |
| issuers | [repeated IssuerCount](#issuercount) | Its queries per issuing tool, highest first, then by issuer, such as how much of a person's work ran through dbt. Empty under a selection. |
| compute\_fraction | [ double](#double) | With a platform: its compute on the platform over the platform's whole compute, or over what the selected entities cost on it, from 0 to 1. The actors overlap, so their fractions do not add up to 1. |
### MeasurePopularity
(synq.entities.activity.v1alpha.MeasurePopularity)
MeasurePopularity is one measure's answer. For RUNS, writes is the runs of what builds the entity,
each counted once whatever orchestrators ran it, last\_written\_at the last run and write\_cadence how
often it runs. RUNS has no reads, no logins and no ranks.
| Field | Type | Description |
| - | - | - |
| measure | [ ActivityMeasure](#activitymeasure) | none |
| reads | [ uint64](#uint64) | Counted reads, and the distinct logins among them. A login is not a person. A read with no login counts in reads, never in reader\_logins. |
| reader\_logins | [ uint32](#uint32) | none |
| last\_read\_at | [ Timestamp](#timestamp) | none |
| writes | [ uint64](#uint64) | none |
| writer\_logins | [ uint32](#uint32) | none |
| last\_written\_at | [ Timestamp](#timestamp) | none |
| write\_cadence | [ WriteCadence](#writecadence) | none |
| member\_entity\_ids | [repeated string](#string) | The entities the same as the asked one whose access answered. |
| ranks | [repeated PoolRank](#poolrank) | The entity's rank in each pool of the asked kind where it has reads. Empty when it has none. |
| readings | [repeated CurrentReading](#currentreading) | The platform's own current figures for the entity, one per direction, shape and window. They are not in reads or writes: a trailing window or a latest value cannot be split into the hours of the window. |
### MostRead
(synq.entities.activity.v1alpha.MostRead)
MostRead is one entity's place in one pool.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The entity, or the smallest id among the entities it is the same as. |
| reads | [ uint64](#uint64) | none |
| rank | [ PoolRank](#poolrank) | none |
### PersonEntityActivity
(synq.entities.activity.v1alpha.PersonEntityActivity)
PersonEntityActivity is what a person's logins did to one entity in one direction and measure.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| direction | [ ActivityDirection](#activitydirection) | none |
| measure | [ ActivityMeasure](#activitymeasure) | none |
| count | [ uint64](#uint64) | none |
| last\_seen\_at | [ Timestamp](#timestamp) | none |
### SchemaSignal
(synq.entities.activity.v1alpha.SchemaSignal)
SchemaSignal is one schema's untouched tables and views.
| Field | Type | Description |
| - | - | - |
| schema | [ WarehouseSchema](#warehouseschema) | none |
| entities | [ uint32](#uint32) | none |
### SignalEntity
(synq.entities.activity.v1alpha.SignalEntity)
SignalEntity is one entity a signal names: an entity with the entities it is the same as, the
tables a share shows in other accounts included, by the smallest id among them, or for UNTOUCHED
the table or view itself. A read of any of them is a read of all, as in popularity. Only the fields
of the signal asked are set.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| write\_queries | [ uint64](#uint64) | BUILT\_NEVER\_READ: the pipeline's write queries and its runs, two measures that never add up, its share of the queries' cost by platform, and when it last wrote. |
| write\_runs | [ uint64](#uint64) | none |
| build\_shares | [repeated PlatformShares](#platformshares) | none |
| last\_written\_at | [ Timestamp](#timestamp) | none |
| schema | [ WarehouseSchema](#warehouseschema) | UNTOUCHED: where it lives. Unset when its address is not known. |
| reads | [ uint64](#uint64) | READ\_WITHOUT\_VIEWER: its counted reads, and those through a shared login. |
| shared\_reads | [ uint64](#uint64) | none |
| outside\_writes | [ uint64](#uint64) | WRITTEN\_OUTSIDE\_PIPELINE: the writes no pipeline explains, and the distinct logins behind them. |
| writing\_logins | [ uint32](#uint32) | none |
### ToolSharedReads
(synq.entities.activity.v1alpha.ToolSharedReads)
ToolSharedReads is one tool's counted reads, and the part of them through a shared login.
| Field | Type | Description |
| - | - | - |
| issuer | [ DataPlatformType](#dataplatformtype) | none |
| reads | [ uint64](#uint64) | none |
| shared\_reads | [ uint64](#uint64) | none |
| share | [ double](#double) | shared\_reads over reads, from 0 to 1. |
### UsageRole
(synq.entities.activity.v1alpha.UsageRole)
UsageRole is one actor in one role of an entity. Coalesce Quality's own queries are never a role.
| Field | Type | Description |
| - | - | - |
| role | [ UsageRoleKind](#usagerolekind) | none |
| level | [ UsageLevel](#usagelevel) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.issuer\_entity\_id | [ string](#string) | For ENTITY: the job, task, step, model or app, such as "dbt model M builds this". |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) actor.service\_account\_id | [ string](#string) | For SERVICE\_ACCOUNT: the service account, a built-in one included. |
| issuer | [ DataPlatformType](#dataplatformtype) | The tool, such as Fivetran, Airflow, dbt or Looker Studio. Unspecified on a PEOPLE, LOGINS or UNKNOWN role, and on a service account whose queries name no tool. |
| built\_in | [ bool](#bool) | Set when service\_account\_id names a built-in account. |
| entity\_id | [ string](#string) | For a service account, its entity id, which resolves to its name like any other entity. |
| evidence | [ UsageEvidence](#usageevidence) | none |
| count | [ uint64](#uint64) | Runs, writes or reads, as evidence says. 0 for IDENTITY. |
| actors | [ uint32](#uint32) | The people behind a PEOPLE role, a person once however many logins they used, or the logins behind a LOGINS role. |
| last\_seen\_at | [ Timestamp](#timestamp) | none |
| cadence | [ WriteCadence](#writecadence) | How often the actor did it in the window. NONE for IDENTITY. |
| counts\_for\_popularity | [ bool](#bool) | On a READ or REVERSE\_ETL role: whether some of its reads count toward popularity. False when the service account's popularity policy leaves them all out, and always false on the other roles. |
| more | [ uint32](#uint32) | How many roles of this kind top\_k cut, set on the last one kept. |
### WarehouseSchema
(synq.entities.activity.v1alpha.WarehouseSchema)
WarehouseSchema is a schema of one warehouse instance, as the warehouse names it.
| Field | Type | Description |
| - | - | - |
| instance | [ string](#string) | The instance, such as a Snowflake account or a BigQuery project. |
| database | [ string](#string) | none |
| schema | [ string](#string) | none |
### BatchOrchestrationRequest
(synq.entities.orchestration.v1.BatchOrchestrationRequest)
| Field | Type | Description |
| - | - | - |
| entities | [repeated Identifier](#identifier) | Entities to query for orchestration relationships (e.g., Airflow tasks, dbt Cloud job steps, Coalesce jobs, dbt models, tables) |
### BatchOrchestrationResponse
(synq.entities.orchestration.v1.BatchOrchestrationResponse)
| Field | Type | Description |
| - | - | - |
| orchestrations | [repeated Orchestration](#orchestration) | Orchestration relationships for each requested entity, in the order of the request. An entity requested twice is answered once, and an entity the workspace does not hold is left out. |
### Orchestration
(synq.entities.orchestration.v1.Orchestration)
Orchestration represents the orchestration relationships for a given entity: what ran it
(upstream) and what it ran (downstream), in the last 31 days. A group that is both is in both
lists. Groups are ordered by how many times they ran, most first.
| Field | Type | Description |
| - | - | - |
| entity | [ OrchestrationGroup](#orchestrationgroup) | The entity being queried along with its other identities. For example, if querying a dbt model, this group includes both the model and the table it creates, as they represent the same logical concept. |
| downstream | [repeated OrchestrationGroup](#orchestrationgroup) | Downstream: the entities this job, job step or task ran in the last 31 days. Each group contains all identities of the entity that ran. Examples: - If entity is an Airflow task: groups containing \[dbt model, table], or the dbt seeds and snapshots it ran - If entity is an Airflow DAG: groups containing its tasks - If entity is a dbt Cloud job step: groups containing \[dbt model, table] - If entity is a Coalesce job: groups containing its Coalesce nodes |
| upstream | [repeated OrchestrationGroup](#orchestrationgroup) | Upstream: the jobs, job steps and tasks that ran this entity in the last 31 days. Each group contains all identities of the job, job step or task. Examples: - If entity is a dbt model or table: groups containing the Airflow task or dbt Cloud job step that ran it - If entity is a Coalesce node: groups containing the Coalesce job that ran it - If entity is an Airflow task: groups containing its Airflow DAG |
### OrchestrationGroup
(synq.entities.orchestration.v1.OrchestrationGroup)
OrchestrationGroup represents different identities of the same logical concept.
For example, a dbt model and the table it creates are different representations
of the same entity, so they appear together in one group. An Airflow task
typically has only one identity (the task itself).
| Field | Type | Description |
| - | - | - |
| entities | [repeated Identifier](#identifier) | Identifiers of all representations of this logical entity. Examples: - For a dbt model: \[dbt model, table it creates] - For an Airflow task: \[Airflow task] |
| synq\_paths | [repeated string](#string) | Coalesce Quality paths of the entities for easier reference, in the same order as entities |
### BatchGetCheckCategoriesRequest
(synq.entities.checks.v2.BatchGetCheckCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Checks to read (1–500, unique). |
### BatchGetCheckCategoriesResponse
(synq.entities.checks.v2.BatchGetCheckCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| categories | [map CategoriesEntry](#categoriesentry) | Category state keyed by check id. Ids that do not exist are absent. |
### BatchGetCheckCategoriesResponse.CategoriesEntry
(synq.entities.checks.v2.BatchGetCheckCategoriesResponse.CategoriesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ CheckCategories](#checkcategories) | none |
### DeleteCheckCategoriesRequest
(synq.entities.checks.v2.DeleteCheckCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategoryDeletion](#checkcategorydeletion) | The checks to clear. Every entry must name a distinct check. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Who is making the change. See UpsertCheckCategoriesRequest.actor. |
### DeleteCheckCategoriesResponse
(synq.entities.checks.v2.DeleteCheckCategoriesResponse)
### ListCategoriesRequest
(synq.entities.checks.v2.ListCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_dimension.dimension | [optional CategoryDimension](#categorydimension) | Restrict to one dimension. Omit to return both. |
### ListCategoriesResponse
(synq.entities.checks.v2.ListCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CategoryInfo](#categoryinfo) | Every category name in use, sorted by name within each dimension. |
| dimensions | [repeated DimensionSummary](#dimensionsummary) | Coverage per dimension, so "N of M checks categorised" can be rendered without summing the entries above. |
### ListCategorisationFiltersRequest
(synq.entities.checks.v2.ListCategorisationFiltersRequest)
| Field | Type | Description |
| - | - | - |
| selected | [ CategorisationFacets](#categorisationfacets) | The values already chosen, if any. Each returned list is narrowed to values that co-occur with this selection. |
### ListCategorisationFiltersResponse
(synq.entities.checks.v2.ListCategorisationFiltersResponse)
| Field | Type | Description |
| - | - | - |
| available | [ CategorisationFacets](#categorisationfacets) | The values available given `selected`. |
### ListCheckCategoriesRequest
(synq.entities.checks.v2.ListCheckCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| technical\_filter | [ CategorisationFilter](#categorisationfilter) | Keep only checks that are, or are not, categorised on the technical dimension. |
| governance\_filter | [ CategorisationFilter](#categorisationfilter) | Keep only checks that are, or are not, categorised on the governance dimension. |
| technical\_override\_filter | [ OverrideFilter](#overridefilter) | Keep only checks whose technical category was, or was not, set explicitly. |
| governance\_override\_filter | [ OverrideFilter](#overridefilter) | Keep only checks whose governance category was, or was not, set explicitly. |
| rule\_ids | [repeated string](#string) | Keep only checks currently categorised by one of these rules, on either dimension. This is the paginated companion to ChecksCategorisationRulesService.ListRuleMatches, which reports exact counts and a sample per rule. |
| technical\_categories | [repeated string](#string) | Keep only checks whose effective technical category is one of these. |
| governance\_categories | [repeated string](#string) | Keep only checks whose effective governance category is one of these. |
| attributes | [ CategorisationFacets](#categorisationfacets) | Keep only checks with these attributes — the same platform / entity type / package / kind selection a categorisation rule matches on, so a listing can be narrowed to exactly the checks a candidate rule would consider. |
| entity\_ids | [repeated string](#string) | Keep only these checks. Combines with the other filters, so it answers "which of these checks are uncategorised" in a single call. |
| pagination | [ Pagination](#pagination) | Pagination. Defaults to 200 checks per page. |
### ListCheckCategoriesResponse
(synq.entities.checks.v2.ListCheckCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategories](#checkcategories) | The matching checks, ordered by entity id. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. Empty when this is the last page. |
### UpsertCheckCategoriesRequest
(synq.entities.checks.v2.UpsertCheckCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategoryUpdate](#checkcategoryupdate) | The checks to categorise. Every entry must name a distinct check. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Who is making the change, when the caller acts on someone's behalf — an agent, a bot, an automation. Recorded as the editor and shown back on reads. Omit it when the caller's own credentials already identify them. |
### UpsertCheckCategoriesResponse
(synq.entities.checks.v2.UpsertCheckCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategories](#checkcategories) | The resolved category state of each updated check, in request order. |
### CategorisationRule
(synq.entities.checks.v2.CategorisationRule)
A rule that assigns a category to every check it matches.
Rules are how a workspace categorises at scale: they apply to the checks that
exist today and to every check ingested afterwards, so nobody has to revisit
individual checks as tools publish new ones.
Rules resolve independently per dimension, and exactly one rule wins a check.
Rules are tried in this order, and the first whose matcher matches assigns
the category:
1. Scope first. Every workspace rule is tried before any platform-provided
one, whatever their priorities are — so a workspace rule at priority 0
still beats a global rule at priority 1000. This is what makes your own
rules an override of the defaults rather than a competitor to them.
2. Then priority, highest number first. `priority` is a rank, not a
position: 10 is tried before 1.
3. Then, among rules of equal scope and priority, most recently updated
first, and finally by id. `resolution_order` reports the resulting
position, so you never have to reproduce this ordering yourself.
A category a rule computes is used only when the check has neither a category
set explicitly on it nor one its producer declared.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Client-supplied UUID identifying the rule. |
| dimension | [ CategoryDimension](#categorydimension) | The dimension this rule categorises along. |
| scope | [ RuleScope](#rulescope) | Whether the rule belongs to the workspace or is a platform default. |
| title | [ string](#string) | Human-readable title, shown wherever the rule is credited for a category. |
| category | [ string](#string) | The category assigned to matching checks. |
| matcher | [ CheckMatcher](#checkmatcher) | Which checks the rule applies to. |
| priority | [ int32](#int32) | Rank within the rule's scope: the highest number is tried first. Ties are broken as described above. |
| resolution\_order | [ int32](#int32) | Position of this rule in the order rules are tried for its dimension, counting from 0 across both scopes — 0 is the rule that gets first refusal on every check. Output-only, and recomputed whenever any rule in the dimension changes. |
| updated\_by | [ Actor](#actor) | Who last edited the rule: display name, and the account or channel they acted through. Absent on a platform-provided rule, which nobody in your workspace edited, and on a rule whose editor could not be resolved to anything a reader would recognise. Output-only. |
| etag | [ string](#string) | Optimistic-concurrency token: pass it back on UpsertRule or DeleteRule to have the write rejected if the rule changed in the meantime. Form: `W/"-"`. |
| created\_at | [ Timestamp](#timestamp) | When the rule was created. Output-only. |
| updated\_at | [ Timestamp](#timestamp) | When the rule was last updated. Output-only. |
### CheckCategories
(synq.entities.checks.v2.CheckCategories)
The category state of a single check, on both dimensions.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The check this describes. Treat it as opaque: acquire it from a listing or from IdentifierResolveService and pass it back unchanged. |
| technical | [ ResolvedCategory](#resolvedcategory) | Technical category — what kind of check this is, mechanically. |
| governance | [ ResolvedCategory](#resolvedcategory) | Governance category — what the check is for. |
| check | [ CheckSummary](#checksummary) | What the check is, so a listing is actionable on its own. Absent for a check that carries an explicit category but is not (or is no longer) present in the catalogue. |
| etag | [ string](#string) | Optimistic-concurrency token covering the categories set explicitly on this check. Pass it back on UpsertCheckCategories or DeleteCheckCategories to have the write rejected if someone changed them in the meantime. Empty when the check carries no explicit category. Form: `W/"-"`. |
| updated\_by | [ Actor](#actor) | Who last set the explicit categories: display name, and the account or channel they acted through. Absent when no explicit categories are set, and when the setter could not be resolved to anything a reader would recognise. |
| updated\_at | [ Timestamp](#timestamp) | When the explicit categories were last changed. Absent when none are set. |
### CheckCategoryDeletion
(synq.entities.checks.v2.CheckCategoryDeletion)
One check whose explicit categories should be cleared, on both dimensions.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The check to clear. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard, with the same meaning as on CheckCategoryUpdate. |
### CheckCategoryUpdate
(synq.entities.checks.v2.CheckCategoryUpdate)
The categories to set explicitly on one check.
Each dimension is updated independently, and presence is meaningful: omit a
field to leave that dimension as it is, send an empty string to clear it so
the check falls back to its producer-declared or rule-computed value, send a
non-empty string to set or replace it.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The check to categorise. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_technical.technical | [optional string](#string) | Technical category to set — what kind of check this is, mechanically. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance.governance | [optional string](#string) | Governance category to set — what the check is for. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict if the check's explicit categories changed since this etag was read. Pass an empty string to require that the check has no explicit category yet, which turns the write into a create-only operation. |
### ComputedCategory
(synq.entities.checks.v2.ComputedCategory)
What the workspace's categorisation rules compute for a check on one
dimension, whatever ends up in effect.
| Field | Type | Description |
| - | - | - |
| category | [ string](#string) | The category the rule assigns. |
| rule\_id | [ string](#string) | The rule that assigns it. Pass it to BatchGetRules to read the rule, or to ListCheckCategories to list every check the rule categorised. |
| rule\_title | [ string](#string) | Title of that rule, so it can be named without a second call. |
### ResolvedCategory
(synq.entities.checks.v2.ResolvedCategory)
The category a check resolves to on one dimension, together with where that
value came from.
Reading `source` matters as much as reading `category`: a value that came
from a rule changes when the rules change, while one set explicitly on the
check stays until someone clears it.
| Field | Type | Description |
| - | - | - |
| category | [ string](#string) | The category in effect. Empty when the check is uncategorised on this dimension. |
| source | [ CategorySource](#categorysource) | Which of the three sources produced `category`. |
| override | [ string](#string) | The category set explicitly on the check, if any. Populated whatever the effective value is, so you can tell an explicit category apart from the one it replaced. Empty when none is set. |
| rule\_id | [ string](#string) | The rule that computed the category. Populated only when `source` is CATEGORY\_SOURCE\_RULE; pass it to BatchGetRules to read the rule, or to ListCheckCategories to list every check the rule categorised. |
| rule\_title | [ string](#string) | Title of the rule named by `rule_id`, so "categorised by …" can be rendered without a second call. |
| rule\_scope | [ RuleScope](#rulescope) | Whether the rule that won is one of the workspace's own or a platform-provided default. Populated only when `source` is CATEGORY\_SOURCE\_RULE. |
| computed | [ ComputedCategory](#computedcategory) | What the categorisation rules compute for this check, populated whenever a rule matches it at all. |
When `source` is CATEGORY\_SOURCE\_RULE this is the value in effect and repeats `category`. When a category set explicitly on the check, or one its producer declared, is standing in front of the rules, this is what the check falls back to once that is cleared — the one part of the resolution a client cannot work out from the fields above. Absent when no rule matches. |
### AnnotationMatch
(synq.entities.checks.v2.AnnotationMatch)
A condition on one annotation of a check.
Annotations are the key/value metadata a producer attaches to a check — a
dbt test's `meta` entries, for example — which makes them the way to
categorise by something only your own team knows, such as a domain or an
owning squad.
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | Annotation key to look up on the check. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) match.exists | [ bool](#bool) | Matches when the key is present, whatever its value. Set it to true; a false value is not a negation and is rejected. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) match.equals | [ string](#string) | Matches when the value equals this string exactly. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) match.in | [ AnnotationValues](#annotationvalues) | Matches when the value is one of these. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) match.regex | [ string](#string) | Matches when the value matches this RE2 regular expression. |
### AnnotationValues
(synq.entities.checks.v2.AnnotationValues)
A list of accepted annotation values.
| Field | Type | Description |
| - | - | - |
| values | [repeated string](#string) | none |
### CheckAttributesMatcher
(synq.entities.checks.v2.CheckAttributesMatcher)
Selects checks by their attributes. A check matches when every populated
field matches it; an empty field places no constraint, so an entirely empty
matcher matches every check in the workspace.
This is the form to reach for first: it is what the rule editor writes, what
reads back legibly, and what ListCategorisationFilters offers values for.
| Field | Type | Description |
| - | - | - |
| facets | [ CategorisationFacets](#categorisationfacets) | Platform, entity type, package and kind the check must be one of. |
| names | [repeated string](#string) | Patterns matched against the check's name. `*` stands for any sequence of characters, including none; every other character matches literally. A check matches when at least one pattern matches. An empty list matches any name. |
| annotation\_matches | [repeated AnnotationMatch](#annotationmatch) | Conditions on the check's annotations. A check must satisfy every entry. |
### CheckMatcher
(synq.entities.checks.v2.CheckMatcher)
Which checks a categorisation rule applies to.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) matcher.attributes | [ CheckAttributesMatcher](#checkattributesmatcher) | Select checks by their attributes. Covers almost every rule and is the only form the rule editor can render. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) matcher.expression | [ string](#string) | A CEL expression returning a boolean, for the rare predicate the attribute matcher cannot express — combining alternatives across dimensions, or matching on a substring of a name. See [https://github.com/google/cel-spec](https://github.com/google/cel-spec) for the language. |
Variables available to the expression: platform, asset\_type, package, kind, name, description (string) is\_platform\_native (bool) annotations (map\)
Platform and entity-type enum value names are available as string constants. For example: platform == PLATFORM\_DBT && kind.startsWith("not\_null") |
### CheckSummary
(synq.entities.checks.v2.CheckSummary)
Enough of a check to recognise it and act on it without a second call.
Every field is output-only and reflects the check as the tool that produced
it described it — a dbt test, a monitor, a reconciliation case. Nothing here
is editable through the categorisation API; to change any of it, change it in
the tool that publishes the check.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the check, as its producer names it. Always the last element of `full_name` when that is populated; kept as its own field so a client that only needs the label never has to index into an array. |
| full\_name | [repeated string](#string) | Full display path down to the check, root first and leaf last — suitable for rendering a breadcrumb as-is, the same order and meaning as `synq.entities.folders.v1.Folder.full_name`: `["prod", "analytics", "orders", "not_null_customer_id"]`. |
The leading segments are the producer's own containers — a dbt project, a warehouse database and schema — so how many there are varies by platform. Drop the last element for the path to the check's parent. |
\| package | [ string](#string) | Package the check comes from, e.g. "dbt", "dbt\_utils". Empty when the producer has no notion of packages. |
\| kind | [ string](#string) | General kind of the check, e.g. "not\_null", "unique", "row\_count". This is the attribute categorisation rules most often match on. |
\| entity\_type | [ EntityType](#entitytype) | Entity type of the check itself, e.g. ENTITY\_TYPE\_DBT\_TEST. |
\| data\_platform\_type | [ DataPlatformType](#dataplatformtype) | Platform the check runs on, e.g. DATA\_PLATFORM\_TYPE\_DBT. |
\| is\_platform\_native | [ bool](#bool) | Whether the check is native to its platform (a dbt test in a dbt project) rather than defined in Coalesce Quality. |
\| description | [ string](#string) | Description of the check, where the producer supplied one. |
\| checked\_entity\_ids | [repeated string](#string) | The entities the check validates. Empty when the check's targets could not be resolved. |
\| annotations | [map AnnotationsEntry](#annotationsentry) | Free-form key/value metadata the tool that produced the check attached to it — a dbt test's `meta` entries, a SQLMesh audit's, a custom label.
This is what a categorisation rule's `annotation_matches` predicate is written against, so reading them is how you find out what there is to match on in your own workspace. |
### CheckSummary.AnnotationsEntry
(synq.entities.checks.v2.CheckSummary.AnnotationsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### BatchGetRulesRequest
(synq.entities.checks.v2.BatchGetRulesRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Rule ids to read (1–200, unique). |
### BatchGetRulesResponse
(synq.entities.checks.v2.BatchGetRulesResponse)
| Field | Type | Description |
| - | - | - |
| rules | [map RulesEntry](#rulesentry) | Rules keyed by id. Ids the caller cannot see, or that do not exist, are absent. |
### BatchGetRulesResponse.RulesEntry
(synq.entities.checks.v2.BatchGetRulesResponse.RulesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ CategorisationRule](#categorisationrule) | none |
### DeleteRuleRequest
(synq.entities.checks.v2.DeleteRuleRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the rule to delete. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Who is making the change. See UpsertRuleRequest.actor. |
### DeleteRuleResponse
(synq.entities.checks.v2.DeleteRuleResponse)
### GetRecategorisationStatusRequest
(synq.entities.checks.v2.GetRecategorisationStatusRequest)
### GetRecategorisationStatusResponse
(synq.entities.checks.v2.GetRecategorisationStatusResponse)
| Field | Type | Description |
| - | - | - |
| status | [ RecategorisationStatus](#recategorisationstatus) | Progress of the caller's workspace sweep. |
### ListRuleMatchesRequest
(synq.entities.checks.v2.ListRuleMatchesRequest)
| Field | Type | Description |
| - | - | - |
| rule\_ids | [repeated string](#string) | Rules to report on. Empty returns every rule that currently categorises at least one check. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sample\_size.sample\_size | [optional int32](#int32) | How many check ids to return per rule as a sample. Defaults to 100, and may be 0 for counts only. The counts are always exact whatever this is set to. |
### ListRuleMatchesResponse
(synq.entities.checks.v2.ListRuleMatchesResponse)
| Field | Type | Description |
| - | - | - |
| matches | [map MatchesEntry](#matchesentry) | Matches keyed by rule id. Rules that currently categorise nothing are absent. |
### ListRuleMatchesResponse.MatchesEntry
(synq.entities.checks.v2.ListRuleMatchesResponse.MatchesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ RuleMatches](#rulematches) | none |
### ListRulesRequest
(synq.entities.checks.v2.ListRulesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_dimension.dimension | [optional CategoryDimension](#categorydimension) | Restrict to one dimension. Omit to return both. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scope.scope | [optional RuleScope](#rulescope) | Restrict to the workspace's own rules or to the platform-provided ones. Omit to return both. |
| pagination | [ Pagination](#pagination) | Pagination. Defaults to 200 rules per page. |
### ListRulesResponse
(synq.entities.checks.v2.ListRulesResponse)
| Field | Type | Description |
| - | - | - |
| rules | [repeated CategorisationRule](#categorisationrule) | Matching rules, in the order they are tried: workspace rules before platform-provided ones, then by descending priority. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. Empty when this is the last page. |
### PreviewRuleRequest
(synq.entities.checks.v2.PreviewRuleRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_id.id | [optional string](#string) | Set to an existing rule's id to preview an edit to it — the candidate replaces that rule during resolution, so the result reflects the change rather than the addition of a second rule. Omit to preview a new rule. |
| dimension | [ CategoryDimension](#categorydimension) | The dimension the candidate would categorise along. |
| scope | [ RuleScope](#rulescope) | Scope the candidate would be saved with. Defaults to RULE\_SCOPE\_WORKSPACE. |
| title | [ string](#string) | Title the candidate would be saved with. Does not affect the outcome. |
| category | [ string](#string) | The category the candidate would assign. |
| matcher | [ CheckMatcher](#checkmatcher) | Which checks the candidate would apply to. |
| priority | [ int32](#int32) | Priority the candidate would be saved with. It participates in resolution exactly as it would once saved, so previewing at the wrong priority reports the wrong impact. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sample\_size.sample\_size | [optional int32](#int32) | How many check ids to return in each of the response's samples — it caps `affected_entity_ids` and `explicit_category_entity_ids` independently, so either may come back at this size. Defaults to 100, and may be 0 for counts only. The counts are always exact whatever this is set to. |
### PreviewRuleResponse
(synq.entities.checks.v2.PreviewRuleResponse)
| Field | Type | Description |
| - | - | - |
| matched\_count | [ int64](#int64) | Checks whose predicate the candidate matches at all — its raw reach, whether or not it ends up winning them. |
| affected\_count | [ int64](#int64) | Checks whose effective category on the candidate's dimension would actually change. This is the rule's real impact. It equals newly\_categorised\_count + reassigned\_count, and matched\_count equals this plus unchanged\_count. |
| newly\_categorised\_count | [ int64](#int64) | Of the affected checks, how many have no category on this dimension today. |
| reassigned\_count | [ int64](#int64) | Of the affected checks, how many already have a different category that the candidate would take over. Worth a second look before saving. |
| unchanged\_count | [ int64](#int64) | Checks the candidate matches whose effective category would not change. The three fields below say why, and add up to this one. |
| affected\_entity\_ids | [repeated string](#string) | A sample of the affected checks, ordered by entity id. |
| affected\_entity\_ids\_truncated | [ bool](#bool) | True when the sample was cut short by `sample_size`. |
| higher\_priority\_rule\_count | [ int64](#int64) | Of the unchanged checks, how many a rule of higher priority already wins, so the candidate never gets to assign anything. Raise the candidate's priority to take these over. |
| explicit\_category\_count | [ int64](#int64) | Of the unchanged checks, how many carry a category set explicitly on the check, or declared by the tool that produced it. Either outranks every rule, so these are the checks the candidate looks like it covers and does not — no priority will win them. Clear the explicit category to hand a check back to the rules. |
| same\_category\_count | [ int64](#int64) | Of the unchanged checks, how many the candidate does win, assigning the category the check already has. Nothing to do about these; the rule is simply agreeing with what is there. |
| explicit\_category\_entity\_ids | [repeated string](#string) | A sample of the checks counted in `explicit_category_count`, ordered by entity id — the ones worth showing a customer, because a rule that reads as covering them does not. |
| explicit\_category\_entity\_ids\_truncated | [ bool](#bool) | True when that sample was cut short by `sample_size`. |
### UpsertRuleRequest
(synq.entities.checks.v2.UpsertRuleRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Client-supplied UUID identifying the rule. The same id updates the same rule; a new id creates a new rule. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_dimension.dimension | [optional CategoryDimension](#categorydimension) | The dimension the rule categorises along. Required when creating; omit to keep it on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scope.scope | [optional RuleScope](#rulescope) | Scope of the rule. Defaults to RULE\_SCOPE\_WORKSPACE when creating; omit to keep it on update. Platform-provided rules cannot be edited from a workspace. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | Human-readable title, shown wherever the rule is credited for a category. Required when creating; omit to keep it on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | The category assigned to matching checks. Required when creating; omit to keep it on update. Prefer a name the workspace already uses — read them from ChecksCategoriesService.ListCategories. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_matcher.matcher | [optional CheckMatcher](#checkmatcher) | Which checks the rule applies to. Required when creating; omit to keep it on update. Replaces the previous matcher wholesale — there is no partial update within a matcher. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_priority.priority | [optional int32](#int32) | Rank within the rule's scope: the highest number is tried first. Defaults to 0 when creating; omit to keep it on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the write fails with a conflict if the rule was modified since this etag was read. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_actor.actor | [optional Actor](#actor) | Who is making the change, when the caller acts on someone's behalf — an agent, a bot, an automation. Recorded as the editor and shown back on reads. Omit it when the caller's own credentials already identify them. |
### UpsertRuleResponse
(synq.entities.checks.v2.UpsertRuleResponse)
| Field | Type | Description |
| - | - | - |
| rule | [ CategorisationRule](#categorisationrule) | The stored rule after the write. |
### RuleMatches
(synq.entities.checks.v2.RuleMatches)
The checks one categorisation rule currently categorises — the state already
applied to the workspace, not a projection of what a rule would do.
The count is always exact. The sample is a readable excerpt, not the full
set: to page through every check a rule categorised, call
ListCheckCategories with the rule's id.
| Field | Type | Description |
| - | - | - |
| check\_count | [ int64](#int64) | How many checks the rule categorises in the workspace. |
| sample\_entity\_ids | [repeated string](#string) | A sample of those checks, ordered by entity id. |
| sample\_truncated | [ bool](#bool) | True when the sample was cut short, meaning the rule categorises more checks than the sample shows. |
### CategorisationFacets
(synq.entities.checks.v2.CategorisationFacets)
A set of check attributes. The same four dimensions select checks in three
places — the predicate of a categorisation rule, the filter on a check
listing, and the values a workspace actually has — so one message carries
them everywhere and a value read from one can be passed straight to the next.
An empty list places no constraint on its dimension. Within a list the values
are alternatives; across lists they combine.
| Field | Type | Description |
| - | - | - |
| data\_platform\_types | [repeated DataPlatformType](#dataplatformtype) | Platforms the check runs on. |
| entity\_types | [repeated EntityType](#entitytype) | Entity types of the check itself, e.g. ENTITY\_TYPE\_DBT\_TEST. |
| packages | [repeated string](#string) | Packages the check comes from, e.g. "dbt\_utils". |
| kinds | [repeated string](#string) | Check kinds, e.g. "not\_null", "unique". |
### CategoryInfo
(synq.entities.checks.v2.CategoryInfo)
One category name in a workspace's vocabulary, with how it is produced and
how much of the workspace currently carries it.
Category names are free-form workspace strings, not a fixed list. Offering
this vocabulary in a picker rather than a text box is what keeps a workspace
converging on one spelling per concept.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | The category name. |
| dimension | [ CategoryDimension](#categorydimension) | The dimension the name belongs to. The same name can exist on both dimensions; they are separate entries. |
| has\_workspace\_rule | [ bool](#bool) | At least one rule in the caller's workspace produces this category. |
| has\_global\_rule | [ bool](#bool) | At least one platform-provided rule produces this category. |
| check\_count | [ int64](#int64) | How many checks currently resolve to this category on this dimension, whatever produced it. A name can have rules and no checks (nothing matches yet), or checks and no rules (every one of them was categorised explicitly or by its producer). |
### DimensionSummary
(synq.entities.checks.v2.DimensionSummary)
How much of a workspace one dimension covers.
| Field | Type | Description |
| - | - | - |
| dimension | [ CategoryDimension](#categorydimension) | The dimension summarised. |
| categorised\_check\_count | [ int64](#int64) | Checks in the workspace with a category on this dimension. |
| uncategorised\_check\_count | [ int64](#int64) | Checks in the workspace with no category on this dimension. This is the number to drive down. |
### RecategorisationStatus
(synq.entities.checks.v2.RecategorisationStatus)
Progress of the workspace-wide re-categorisation that follows a rule change.
A rule change takes effect on newly ingested checks immediately. Checks that
already exist are rewritten by a sweep, scheduled automatically whenever
rules are created, updated or deleted; several edits in quick succession
coalesce into one sweep. The sweep normally completes in seconds, so reading
this is about confirming a change landed rather than waiting on it — until it
finishes, some checks still carry the categories the previous rules gave
them.
| Field | Type | Description |
| - | - | - |
| state | [ State](#state) | Current state of the sweep. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scheduled\_at.scheduled\_at | [optional Timestamp](#timestamp) | When the pending sweep was queued. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_started\_at.started\_at | [optional Timestamp](#timestamp) | When the current or most recent sweep started. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_finished\_at.finished\_at | [optional Timestamp](#timestamp) | When the most recent sweep finished. |
| scanned\_count | [ int64](#int64) | Checks examined by the current or most recent sweep. |
| rewritten\_count | [ int64](#int64) | Checks whose categorisation actually changed in that sweep. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_error.last\_error | [optional string](#string) | Populated when the most recent sweep failed. The next rule change schedules a fresh sweep. |
### CheckCategory
(synq.entities.checks.v1.CheckCategory)
CheckCategory is the complete explicit category state for one check,
carrying both dimensions. It overrides the categories the
categorisation rules would otherwise compute for the check.
| Field | Type | Description |
| - | - | - |
| check\_path | [ string](#string) | check\_path of the check this applies to (matches the check's identifier in the catalogue). |
| category | [ string](#string) | Explicit technical category (empty = no explicit technical category; the rule-computed one is used instead). |
| governance\_category | [ string](#string) | Explicit governance category (empty = no explicit governance category; the rule-computed one is used instead). |
| updated\_by | [ Actor](#actor) | Who last set this category: display name, and the account or channel they acted through. Absent when the setter could not be resolved to anything a reader would recognise. Server-derived; ignored on input. |
| updated\_at | [ Timestamp](#timestamp) | When this category was last updated. Server-set; ignored on input. |
### DeleteCheckCategoriesRequest
(synq.entities.checks.v1.DeleteCheckCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| check\_paths | [repeated string](#string) | check\_paths whose explicit categories (both dimensions) should be cleared. |
### DeleteCheckCategoriesResponse
(synq.entities.checks.v1.DeleteCheckCategoriesResponse)
### ListCheckCategoriesRequest
(synq.entities.checks.v1.ListCheckCategoriesRequest)
### ListCheckCategoriesResponse
(synq.entities.checks.v1.ListCheckCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategory](#checkcategory) | none |
### UpsertCheckCategoriesRequest
(synq.entities.checks.v1.UpsertCheckCategoriesRequest)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategory](#checkcategory) | none |
### UpsertCheckCategoriesResponse
(synq.entities.checks.v1.UpsertCheckCategoriesResponse)
| Field | Type | Description |
| - | - | - |
| categories | [repeated CheckCategory](#checkcategory) | Echoed categories with the server-set editor and timestamp. |
### GetCoverageRequest
(synq.entities.checks.v1.GetCoverageRequest)
GetCoverageRequest contains the parameters needed to retrieve check coverage for a specific entity.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get checks coverage for. This can be a path identifier that uniquely identifies the entity in the system. |
| propagation\_filter | [ PropagationFilter](#propagationfilter) | Filter to control which checks are included in the coverage response. Default (UNSPECIFIED) returns both direct and propagated checks. |
| include\_segments | [ bool](#bool) | Include the per-segment checks of segmented monitors. |
A segmented monitor publishes one check entity per distinct value of its segmentation column, so a single monitor can stand behind hundreds of them and they can outnumber an asset's other checks by an order of magnitude. They are left out by default so a coverage read stays readable, and asked for explicitly when the caller wants the individual segment ids — the same ids alerts, search results and the monitor-predictions read use.
Segment checks are only ever reported on the asset the monitor runs on: they are a slice of one monitor on one asset, and never propagate downstream. The parent monitor's own check propagates as it always has, so downstream coverage is unchanged by this field. |
### GetCoverageResponse
(synq.entities.checks.v1.GetCoverageResponse)
GetCoverageResponse contains the check coverage information for the requested entity.
| Field | Type | Description |
| - | - | - |
| coverage | [ ChecksSummary](#checkssummary) | The complete coverage information for the requested entity. |
### GetCoverageResponse.ChecksSummary
(synq.entities.checks.v1.GetCoverageResponse.ChecksSummary)
ChecksSummary provides a comprehensive overview of checks and their coverage.
| Field | Type | Description |
| - | - | - |
| all\_columns | [repeated string](#string) | List of all column names in the entity. |
| table\_checks | [repeated CheckEntity](#checkentity) | List of table-level checks applied to this entity. |
| propagated\_table\_checks | [repeated PropagatedTableCheck](#propagatedtablecheck) | List of table-level checks propagated from upstream entities. |
| column\_info | [repeated ColumnInformation](#columninformation) | Detailed information about each column in the entity. |
### GetCoverageResponse.ChecksSummary.ColumnInformation
(synq.entities.checks.v1.GetCoverageResponse.ChecksSummary.ColumnInformation)
ColumnInformation contains detailed information about a specific column.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human readable name of the column as present in dbt or data warehouse. |
| native\_type | [ string](#string) | Native data type of the column as present in data warehouse. |
| direct\_check\_entities | [repeated CheckEntity](#checkentity) | Checks running on current model on this column |
| propagated\_check\_entities | [repeated PropagatedColumnCheck](#propagatedcolumncheck) | Checks running on upstream model column, where this is a passthrough of it |
| is\_transformed | [ bool](#bool) | Indicates that there is an expression transforming the data |
| is\_renamed | [ bool](#bool) | Indicates that there is a rename operation |
| is\_passthrough | [ bool](#bool) | Indicates that there is no transformation or rename |
| used\_by\_downstream | [ bool](#bool) | Is this column used by any downstream model column |
| referenced\_by\_downstream | [ bool](#bool) | Is any downstream model referencing this column e.g. in WHERE |
| using\_upstream | [ bool](#bool) | Does this column pull any data from upstream |
| referencing\_upstream | [ bool](#bool) | Does this column reference any columns in upstream |
| downstream\_usage | [ ColumnUsage](#columnusage) | Statistics about column impact |
### GetCoverageResponse.ColumnUpstream
(synq.entities.checks.v1.GetCoverageResponse.ColumnUpstream)
ColumnUpstream represents an upstream column that a check is propagated from.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the upstream entity. |
| column | [ string](#string) | Name of the column in the upstream entity. |
### GetCoverageResponse.ColumnUsage
(synq.entities.checks.v1.GetCoverageResponse.ColumnUsage)
ColumnUsage contains statistics about how a column is used by downstream entities.
| Field | Type | Description |
| - | - | - |
| used\_as\_data\_direct | [ int32](#int32) | Number of times this column is directly used as data. |
| used\_as\_data\_cumulative | [ int32](#int32) | Total number of times this column is used as data (including indirect usage). |
| used\_as\_logic\_direct | [ int32](#int32) | Number of times this column is directly used in logic (e.g., WHERE clauses). |
| used\_as\_logic\_cumulative | [ int32](#int32) | Total number of times this column is used in logic (including indirect usage). |
### GetCoverageResponse.PropagatedColumnCheck
(synq.entities.checks.v1.GetCoverageResponse.PropagatedColumnCheck)
PropagatedColumnCheck represents a check that is propagated from an upstream column.
| Field | Type | Description |
| - | - | - |
| check | [ CheckEntity](#checkentity) | The check entity being propagated. |
| propagation | [repeated ColumnUpstream](#columnupstream) | List of upstream columns where this check originates from. |
### GetCoverageResponse.PropagatedColumnChecks
(synq.entities.checks.v1.GetCoverageResponse.PropagatedColumnChecks)
PropagatedColumnChecks contains a list of checks propagated from upstream columns.
| Field | Type | Description |
| - | - | - |
| checks | [repeated PropagatedColumnCheck](#propagatedcolumncheck) | List of propagated column checks. |
### GetCoverageResponse.PropagatedTableCheck
(synq.entities.checks.v1.GetCoverageResponse.PropagatedTableCheck)
PropagatedTableCheck represents a check that is propagated from an upstream table.
| Field | Type | Description |
| - | - | - |
| check | [ CheckEntity](#checkentity) | The check entity being propagated. |
| upstream\_ids | [repeated Identifier](#identifier) | List of upstream entity identifiers where this check originates from. |
### CheckEntity
(synq.entities.checks.v1.CheckEntity)
| Field | Type | Description |
| - | - | - |
| check\_id | [ Identifier](#identifier) | Identifier of the check |
| name | [ string](#string) | The name of the check |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_package.package | [optional string](#string) | The package of the check, e.g. "dbt", "sqlmesh", "dbt-utils" |
| kind | [ string](#string) | General kind of the test, e.g. "not\_null", "unique", "foreign\_key" |
| entity\_type | [ EntityType](#entitytype) | Entity type of the check itself, e.g. ENTITY\_TYPE\_DBT\_TEST. ENTITY\_TYPE\_UNSPECIFIED when the type is unknown. |
| data\_platform\_type | [ DataPlatformType](#dataplatformtype) | Platform the check runs on, e.g. DATA\_PLATFORM\_TYPE\_DBT. DATA\_PLATFORM\_TYPE\_UNSPECIFIED when the platform is unknown. |
| is\_platform\_native | [ bool](#bool) | Whether the check is platform native |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Technical category to use for this check — what kind of check it is, mechanically, e.g. "nullness". Empty when the check declares none, in which case the category a categorisation rule computes is used instead. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | Governance category to use for this check — what the check is for, e.g. "Completeness". Empty when the check declares none, in which case the category a categorisation rule computes is used instead. |
### BatchGetPlatformUsersRequest
(synq.entities.platformusers.v1alpha.BatchGetPlatformUsersRequest)
| Field | Type | Description |
| - | - | - |
| platform\_user\_ids | [repeated string](#string) | none |
| window\_days | [ uint32](#uint32) | The days of query history read, back from the current hour. Unset is 30. |
| view | [ PlatformUserView](#platformuserview) | none |
### BatchGetPlatformUsersResponse
(synq.entities.platformusers.v1alpha.BatchGetPlatformUsersResponse)
| Field | Type | Description |
| - | - | - |
| platform\_users | [map PlatformUsersEntry](#platformusersentry) | Keyed by platform user id. |
### BatchGetPlatformUsersResponse.PlatformUsersEntry
(synq.entities.platformusers.v1alpha.BatchGetPlatformUsersResponse.PlatformUsersEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ PlatformUser](#platformuser) | none |
### LinkCandidateMember
(synq.entities.platformusers.v1alpha.LinkCandidateMember)
LinkCandidateMember is one workspace member a login may be linked to.
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
| first\_name | [ string](#string) | none |
| last\_name | [ string](#string) | none |
| email | [ string](#string) | none |
| identities | [repeated string](#string) | The member's identities, as User.identities lists them. A login listed here by its platform\_user\_id is already linked, and is never suggested. |
### LinkSuggestion
(synq.entities.platformusers.v1alpha.LinkSuggestion)
LinkSuggestion is one login that may belong to one member.
| Field | Type | Description |
| - | - | - |
| platform\_user\_id | [ string](#string) | none |
| login | [ string](#string) | none |
| platform | [ DataPlatformType](#dataplatformtype) | none |
| platform\_root\_entity\_id | [ string](#string) | none |
| user\_identity | [ string](#string) | "synq:\" of the suggested member. |
| evidence | [ LinkEvidence](#linkevidence) | none |
| ambiguous | [ bool](#bool) | The login matches more than one member, each listed with its own suggestion. |
### ListLinkSuggestionsRequest
(synq.entities.platformusers.v1alpha.ListLinkSuggestionsRequest)
| Field | Type | Description |
| - | - | - |
| members | [repeated LinkCandidateMember](#linkcandidatemember) | The members to match, as synq.users.v1 UsersService.ListUsers returns them. |
| platform\_user\_ids | [repeated string](#string) | Only these logins. Empty considers every login. |
| pagination | [ Pagination](#pagination) | Without a page size every suggestion is returned. |
### ListLinkSuggestionsResponse
(synq.entities.platformusers.v1alpha.ListLinkSuggestionsResponse)
| Field | Type | Description |
| - | - | - |
| suggestions | [repeated LinkSuggestion](#linksuggestion) | By evidence, strongest first, then login, platform user id and member. |
| page\_info | [ PageInfo](#pageinfo) | none |
### ListPlatformUsersRequest
(synq.entities.platformusers.v1alpha.ListPlatformUsersRequest)
| Field | Type | Description |
| - | - | - |
| service\_account\_ids | [repeated string](#string) | Only the logins in these service accounts, built-in ones included. |
| platforms | [repeated DataPlatformType](#dataplatformtype) | Only the logins on these platforms. |
| platform\_root\_entity\_ids | [repeated string](#string) | Only the logins on these platform instances. |
| unclassified\_only | [ bool](#bool) | Only the logins with a slice no service account takes and no person link or email links. |
| suggested\_only | [ bool](#bool) | Only the logins a service account suggestion includes. |
| search | [ string](#string) | A substring of the login, display name or email, ignoring case. |
| window\_days | [ uint32](#uint32) | The days of query history read, back from the current hour. Unset is 30. |
| platform\_user\_ids | [repeated string](#string) | Only these logins. |
| sources | [repeated string](#string) | Only the logins these integrations declare. |
| view | [ PlatformUserView](#platformuserview) | none |
| pagination | [ Pagination](#pagination) | Without a page size every matching login is returned. |
### ListPlatformUsersResponse
(synq.entities.platformusers.v1alpha.ListPlatformUsersResponse)
| Field | Type | Description |
| - | - | - |
| platform\_users | [repeated PlatformUser](#platformuser) | none |
| summary | [ PlatformUsersSummary](#platformuserssummary) | On the first page only. |
| page\_info | [ PageInfo](#pageinfo) | none |
| from | [ Timestamp](#timestamp) | The window read, \[from, to). |
| to | [ Timestamp](#timestamp) | none |
### ApplyServiceAccountChangesRequest
(synq.entities.platformusers.v1alpha.ApplyServiceAccountChangesRequest)
| Field | Type | Description |
| - | - | - |
| changes | [ ServiceAccountChanges](#serviceaccountchanges) | none |
| plan\_token | [ string](#string) | none |
### ApplyServiceAccountChangesResponse
(synq.entities.platformusers.v1alpha.ApplyServiceAccountChangesResponse)
| Field | Type | Description |
| - | - | - |
| service\_accounts | [repeated ServiceAccount](#serviceaccount) | The accounts and person links as written, with their new etags. |
| person\_links | [repeated PersonLink](#personlink) | none |
### BatchGetServiceAccountsRequest
(synq.entities.platformusers.v1alpha.BatchGetServiceAccountsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### BatchGetServiceAccountsResponse
(synq.entities.platformusers.v1alpha.BatchGetServiceAccountsResponse)
| Field | Type | Description |
| - | - | - |
| service\_accounts | [map ServiceAccountsEntry](#serviceaccountsentry) | The accounts among the asked ids, keyed by id. |
### BatchGetServiceAccountsResponse.ServiceAccountsEntry
(synq.entities.platformusers.v1alpha.BatchGetServiceAccountsResponse.ServiceAccountsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ ServiceAccount](#serviceaccount) | none |
### ListServiceAccountSuggestionsRequest
(synq.entities.platformusers.v1alpha.ListServiceAccountSuggestionsRequest)
| Field | Type | Description |
| - | - | - |
| window\_days | [ uint32](#uint32) | The days of query history the evidence is read over, back from the current hour. Unset is 30. |
### ListServiceAccountSuggestionsResponse
(synq.entities.platformusers.v1alpha.ListServiceAccountSuggestionsResponse)
| Field | Type | Description |
| - | - | - |
| suggestions | [repeated ServiceAccountSuggestion](#serviceaccountsuggestion) | By source, then by how much of the logins' activity carries the evidence, highest first. |
### ListServiceAccountsRequest
(synq.entities.platformusers.v1alpha.ListServiceAccountsRequest)
### ListServiceAccountsResponse
(synq.entities.platformusers.v1alpha.ListServiceAccountsResponse)
| Field | Type | Description |
| - | - | - |
| service\_accounts | [repeated ServiceAccount](#serviceaccount) | The workspace's accounts by priority, highest first, then the last update, newest first, then id. Then the built-in accounts, in the order they apply, `built_in.coalesce-quality` first. |
| person\_links | [repeated PersonLink](#personlink) | By priority, highest first, then the last update, newest first, then id. |
### PlanServiceAccountChangesRequest
(synq.entities.platformusers.v1alpha.PlanServiceAccountChangesRequest)
| Field | Type | Description |
| - | - | - |
| changes | [ ServiceAccountChanges](#serviceaccountchanges) | none |
| window\_days | [ uint32](#uint32) | The days of query history the plan is read over, back from the current hour. Unset is 30. |
### PlanServiceAccountChangesResponse
(synq.entities.platformusers.v1alpha.PlanServiceAccountChangesResponse)
| Field | Type | Description |
| - | - | - |
| slice\_changes | [repeated SliceChange](#slicechange) | By the size of counted\_reads\_delta, largest first, then login and issuer. |
| counted\_reads\_delta | [ int64](#int64) | The counted reads the change set adds, negative when it removes them. |
| rank\_changes | [repeated RankChange](#rankchange) | At most 20, in the SYSTEM pool, by how far the percentile moves, largest first. Empty when the ranks could not be computed, with ranks\_unavailable set. |
| ranks\_unavailable | [ bool](#bool) | none |
| plan\_token | [ string](#string) | What ApplyServiceAccountChanges takes for exactly this change set. |
| from | [ Timestamp](#timestamp) | The window planned over, \[from, to). |
| to | [ Timestamp](#timestamp) | none |
### RankChange
(synq.entities.platformusers.v1alpha.RankChange)
RankChange is one entity whose rank in one pool the change set moves.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| pool | [ PopularityPool](#popularitypool) | none |
| platform | [ DataPlatformType](#dataplatformtype) | none |
| instance | [ string](#string) | none |
| reads\_before | [ uint64](#uint64) | none |
| reads\_after | [ uint64](#uint64) | none |
| percentile\_before | [ double](#double) | From 0 to 1, 0 when the entity was not ranked. |
| percentile\_after | [ double](#double) | none |
### ServiceAccountSuggestion
(synq.entities.platformusers.v1alpha.ServiceAccountSuggestion)
| Field | Type | Description |
| - | - | - |
| source | [ SuggestionSource](#suggestionsource) | none |
| changes | [ ServiceAccountChanges](#serviceaccountchanges) | The account to create, with one matcher. Its ids are generated, so applying it twice creates it once. |
| evidence | [repeated Evidence](#evidence) | Why each login is in it, one entry per login. |
| confidence | [ SuggestionConfidence](#suggestionconfidence) | The least confidence of its logins' evidence. |
### SliceChange
(synq.entities.platformusers.v1alpha.SliceChange)
SliceChange is one login slice whose classification the change set moves.
| Field | Type | Description |
| - | - | - |
| platform\_user\_id | [ string](#string) | none |
| login | [ string](#string) | none |
| issuer | [ DataPlatformType](#dataplatformtype) | The tool whose queries the slice is, DATA\_PLATFORM\_TYPE\_UNSPECIFIED for queries with no tag. |
| before | [ SliceReading](#slicereading) | none |
| after | [ SliceReading](#slicereading) | none |
| counted\_reads\_delta | [ int64](#int64) | Reads of the slice in the window that start counting toward popularity, negative when they stop. |
### SliceReading
(synq.entities.platformusers.v1alpha.SliceReading)
SliceReading is how one login slice reads under a classification.
| Field | Type | Description |
| - | - | - |
| classification | [ Classification](#classification) | none |
| service\_account\_ids | [repeated string](#string) | The service accounts the slice is in, the first one deciding its group, built-in ones included. |
| person\_identity | [ string](#string) | The identity of its person, for a slice a person link or the platform's email links. |
| counts\_toward\_popularity | [ bool](#bool) | none |
| listed\_as\_frequent\_user | [ bool](#bool) | none |
### AdvancedMatch
(synq.entities.platformusers.v1alpha.AdvancedMatch)
AdvancedMatch narrows a matcher by what the platform states about a login and by what the login
did. Every non-empty field must match, and an empty one matches everything.
| Field | Type | Description |
| - | - | - |
| platforms | [repeated DataPlatformType](#dataplatformtype) | The platform of the login. |
| platform\_root\_entity\_ids | [repeated string](#string) | The platform instance the login lives on, such as the Snowflake account's entity id. |
| user\_types | [repeated string](#string) | The kind of login the platform states, such as Snowflake's `SERVICE` or BigQuery's `serviceAccount`, ignoring case. |
| email\_domains | [repeated string](#string) | The domain of the email the platform states, ignoring case. |
| issuers | [repeated DataPlatformType](#dataplatformtype) | The tools whose queries the matcher takes, from the tag a tool puts on its queries. Empty takes every query of a matching login. DATA\_PLATFORM\_TYPE\_UNSPECIFIED names the queries with no tag. |
| min\_issuer\_share | [ double](#double) | The least share of the login's queries in the window, from 0 to 1, that a tool named in issuers must have. Only with issuers. |
| footprints | [repeated DataPlatformType](#dataplatformtype) | Loaders whose columns the tables the login writes carry, such as Fivetran's `_fivetran_synced`. |
| min\_footprint\_share | [ double](#double) | The least share, from 0 to 1, of the tables the login wrote in the window that carry a loader's columns. Only with footprints. |
### BuiltInPolicyChange
(synq.entities.platformusers.v1alpha.BuiltInPolicyChange)
BuiltInPolicyChange sets the popularity policy of a built-in account, the one thing of a built-in
account a workspace can change.
| Field | Type | Description |
| - | - | - |
| built\_in\_id | [ string](#string) | none |
| policy | [ PopularityPolicy](#popularitypolicy) | A policy with neither field set goes back to the purpose default. |
### CoalesceLoginExclusion
(synq.entities.platformusers.v1alpha.CoalesceLoginExclusion)
CoalesceLoginExclusion says whether a former login of Coalesce Quality is still ours. A login that
a Coalesce Quality integration connects as now cannot be excluded.
| Field | Type | Description |
| - | - | - |
| platform\_user\_id | [ string](#string) | none |
| excluded | [ bool](#bool) | True takes the login out of the Coalesce Quality account, false puts it back. |
### Matcher
(synq.entities.platformusers.v1alpha.Matcher)
Matcher selects logins. Set the simple fields first: most service accounts need only login
patterns. Every non-empty field must match.
A matcher with only platform\_user\_ids, and optionally advanced.issuers, is an exact matcher: it
names logins, and wins over every other matcher and over the logins Coalesce Quality connects as.
platform\_user\_ids cannot be combined with any other field, and each login and issuer can be in only
one exact matcher of an account.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Caller-supplied uuid, unique within the workspace. |
| platform\_user\_ids | [repeated string](#string) | Exact logins, by the platform\_user\_id PlatformUsersService returns. |
| logins | [repeated string](#string) | Patterns over the login, `*` for any run of characters, ignoring case, such as `FIVETRAN_*`. |
| emails | [repeated string](#string) | Patterns over the email the platform states, `*` for any run of characters, ignoring case, such as `*@example.com`. On BigQuery the login is the email. |
| advanced | [ AdvancedMatch](#advancedmatch) | none |
| internal\_only | [ bool](#bool) | Output only: the matcher uses a condition this API cannot express. Its account reads as is, and a change to it is refused. |
### PersonLink
(synq.entities.platformusers.v1alpha.PersonLink)
PersonLink links logins to a person: the identity it makes from each login, such as
`email:{login}@example.com`. It applies only to logins no service account takes.
A person link takes only the queries a login ran with no tool's tag, unless its matcher names
issuers. So a person who runs a BI tool on their own credentials keeps those queries as the tool's.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Caller-supplied uuid, unique within the workspace. |
| title | [ string](#string) | none |
| priority | [ int32](#int32) | Higher first, between person links. |
| matcher | [ Matcher](#matcher) | Its id is the link's own, whatever is sent. platform\_user\_ids is not allowed here. |
| identity\_pattern | [ string](#string) | The identity of the person, `{login}` standing for the login, lower-cased. Such as `email:{login}@example.com`, to resolve with synq.users.v1. |
| etag | [ string](#string) | Output only: pass it back on a change of the link. |
| updated\_by | [ Actor](#actor) | Output only. |
| created\_at | [ Timestamp](#timestamp) | Output only. |
| updated\_at | [ Timestamp](#timestamp) | Output only. |
### PopularityPolicy
(synq.entities.platformusers.v1alpha.PopularityPolicy)
PopularityPolicy says whether the reads of a service account's logins count. An unset field takes
the default of the account's purpose: loader, orchestrator, transformer, observability, catalog and
CI reads do not count, every other purpose's do. Coalesce Quality's own queries never count,
whatever a policy says.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_counts\_toward\_popularity.counts\_toward\_popularity | [optional bool](#bool) | Whether the logins' reads count toward an entity's popularity. Unset takes the purpose default. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_listed\_as\_frequent\_user.listed\_as\_frequent\_user | [optional bool](#bool) | Whether the logins may be listed among an entity's frequent users. Unset is false, as a service account is not a person. |
| counts\_toward\_popularity\_resolved | [ bool](#bool) | Output only: what applies, the policy or the purpose default. |
| listed\_as\_frequent\_user\_resolved | [ bool](#bool) | Output only: what applies. |
### ServiceAccount
(synq.entities.platformusers.v1alpha.ServiceAccount)
ServiceAccount is a group of logins that act for one tool or purpose, such as the logins Airflow
runs as. A login slice that a service account takes reads as the tool, not as a person, and counts
toward popularity as the account's policy says.
A login's queries are split by the tool that issued them, from the tag on each query. Each part is
matched on its own, so a person's Looker Studio queries can belong to a shared Looker Studio
account while the rest of their queries stay theirs.
Accounts apply in order: exact matchers first, then every other matcher by the account's priority,
highest first, then the last update, newest first, then the id. Built-in accounts apply after every
workspace account.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | A caller-supplied uuid for a workspace account. A built-in account's id starts with `built_in.`, such as `built_in.fivetran`. |
| name | [ string](#string) | none |
| tool\_platform | [ DataPlatformType](#dataplatformtype) | The tool the logins act for, DATA\_PLATFORM\_TYPE\_UNSPECIFIED when there is none or it is not known. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_tool\_entity\_id.tool\_entity\_id | [optional string](#string) | The tool's integration entity, if the workspace has one. |
| purpose | [ Purpose](#purpose) | none |
| policy | [ PopularityPolicy](#popularitypolicy) | none |
| priority | [ int32](#int32) | Higher first, between accounts. |
| matchers | [repeated Matcher](#matcher) | none |
| built\_in | [ bool](#bool) | Output only: the account is built in. Its matchers cannot be changed, and its policy is changed through ServiceAccountChanges.built\_in\_policies. |
| excluded\_platform\_user\_ids | [repeated string](#string) | Output only, on `built_in.coalesce-quality`: the former logins of Coalesce Quality that a person said are no longer ours. Changed through ServiceAccountChanges.coalesce\_logins. |
| etag | [ string](#string) | Output only: pass it back on a change of the account, so a concurrent change is refused rather than overwritten. |
| updated\_by | [ Actor](#actor) | Output only. |
| created\_at | [ Timestamp](#timestamp) | Output only. |
| updated\_at | [ Timestamp](#timestamp) | Output only. |
### ServiceAccountChanges
(synq.entities.platformusers.v1alpha.ServiceAccountChanges)
ServiceAccountChanges is any mix of changes to a workspace's service accounts, person links and
policies, planned and applied as one.
| Field | Type | Description |
| - | - | - |
| upsert\_service\_accounts | [repeated ServiceAccount](#serviceaccount) | Each replaces the account with its id, matchers included, or creates it. Pass the etag you read to replace one. |
| delete\_service\_account\_ids | [repeated string](#string) | Deleting an account deletes its matchers. Deleting one that is not there succeeds. |
| upsert\_person\_links | [repeated PersonLink](#personlink) | none |
| delete\_person\_link\_ids | [repeated string](#string) | none |
| built\_in\_policies | [repeated BuiltInPolicyChange](#builtinpolicychange) | none |
| coalesce\_logins | [repeated CoalesceLoginExclusion](#coalesceloginexclusion) | none |
### Evidence
(synq.entities.platformusers.v1alpha.Evidence)
Evidence is one hint about what a login is.
| Field | Type | Description |
| - | - | - |
| kind | [ EvidenceKind](#evidencekind) | none |
| platform\_user\_id | [ string](#string) | none |
| login | [ string](#string) | none |
| values | [repeated string](#string) | What the evidence names: the tool, the pattern, the kind, the cadence or the email. |
| text | [ string](#string) | One line for a person, such as "99% Looker Studio queries" or "writes every hour". |
### LoginSlice
(synq.entities.platformusers.v1alpha.LoginSlice)
LoginSlice is the queries of one login that one tool issued, from the tag on each query, or the
queries with no tag. Each slice is classified on its own.
| Field | Type | Description |
| - | - | - |
| issuer | [ DataPlatformType](#dataplatformtype) | DATA\_PLATFORM\_TYPE\_UNSPECIFIED is the queries with no tool's tag. |
| classification | [ Classification](#classification) | none |
| service\_accounts | [repeated SliceMembership](#slicemembership) | Every service account the slice is in, the first one deciding where its queries are grouped. |
| person | [ SlicePerson](#sliceperson) | For a PERSON slice. |
| counts\_toward\_popularity | [ bool](#bool) | none |
| listed\_as\_frequent\_user | [ bool](#bool) | none |
| last\_seen\_at | [ Timestamp](#timestamp) | none |
| signals | [ SliceSignals](#slicesignals) | With view FULL only. |
### PlatformUser
(synq.entities.platformusers.v1alpha.PlatformUser)
PlatformUser is one login on one platform instance.
| Field | Type | Description |
| - | - | - |
| platform\_user\_id | [ string](#string) | Opaque. Pass it back as it is. |
| name | [ string](#string) | The login as the platform shows it. |
| platform | [ DataPlatformType](#dataplatformtype) | none |
| platform\_root\_entity\_id | [ string](#string) | none |
| sources | [repeated PlatformUserSource](#platformusersource) | One per integration that declared the login, by integration. |
| classification | [ Classification](#classification) | none |
| last\_seen\_at | [ Timestamp](#timestamp) | none |
| reads | [ uint64](#uint64) | Reads and writes over the window, Coalesce Quality's own queries left out. |
| writes | [ uint64](#uint64) | none |
| share | [ double](#double) | Its share of the workspace's reads, inputs and writes in the window, from 0 to 1. |
| weights | [repeated PlatformWeights](#platformweights) | What its queries cost, per platform. |
| slices | [repeated LoginSlice](#loginslice) | One per slice, by issuer. |
| identities | [repeated string](#string) | The platform\_user\_id always, which starts with `platform_user:`, and `email:` when an integration lists an email for a login that is not a service kind. |
| evidence | [repeated Evidence](#evidence) | none |
| suggested | [ bool](#bool) | A service account suggestion includes the login. |
### PlatformUserSource
(synq.entities.platformusers.v1alpha.PlatformUserSource)
PlatformUserSource is what one integration declared about a login.
| Field | Type | Description |
| - | - | - |
| source | [ string](#string) | The integration. |
| user\_type | [ string](#string) | The kind of login the platform's user listing states, such as Snowflake's `SERVICE`. |
| email | [ string](#string) | none |
| display\_name | [ string](#string) | none |
| coalesce\_login | [ bool](#bool) | A login the integration connects as, now or before. |
| coalesce\_login\_current | [ bool](#bool) | A login the integration connects as now. |
| state\_at | [ Timestamp](#timestamp) | When the integration observed what it declared. |
### PlatformUsersSummary
(synq.entities.platformusers.v1alpha.PlatformUsersSummary)
PlatformUsersSummary is the whole window of the workspace, whatever the filters.
| Field | Type | Description |
| - | - | - |
| classified\_share | [ double](#double) | The share of the workspace's activity in the window whose login is classified, from 0 to 1. |
| counted\_share | [ double](#double) | The share of the workspace's reads that count toward popularity, from 0 to 1. |
| unclassified\_logins | [ uint32](#uint32) | none |
| suggested\_logins | [ uint32](#uint32) | none |
| logins | [ uint32](#uint32) | none |
### SliceMembership
(synq.entities.platformusers.v1alpha.SliceMembership)
SliceMembership is one service account a login slice is in, and why.
| Field | Type | Description |
| - | - | - |
| service\_account\_id | [ string](#string) | none |
| kind | [ MatchKind](#matchkind) | none |
| matcher\_id | [ string](#string) | The matcher that took the slice, for MATCHER and EXACT. |
### SlicePerson
(synq.entities.platformusers.v1alpha.SlicePerson)
SlicePerson is the person a login slice is linked to.
| Field | Type | Description |
| - | - | - |
| identity | [ string](#string) | Such as `email:jane@example.com`, to resolve with synq.users.v1. |
| person\_link\_id | [ string](#string) | The person link that made it, empty when it is the email the platform states. |
### SliceSignals
(synq.entities.platformusers.v1alpha.SliceSignals)
SliceSignals are what a login slice did in the window: what the matchers and the suggestions see.
| Field | Type | Description |
| - | - | - |
| issuer\_share | [ double](#double) | Its share of the login's queries in the window, from 0 to 1. |
| reads | [ uint64](#uint64) | none |
| inputs | [ uint64](#uint64) | none |
| writes | [ uint64](#uint64) | none |
| active\_days | [ uint32](#uint32) | none |
| distinct\_entities | [ uint32](#uint32) | The distinct entities it touched, and wrote. |
| written\_entities | [ uint32](#uint32) | none |
| write\_cadence | [ WriteCadence](#writecadence) | How often it writes. |
| read\_hours | [ uint32](#uint32) | The distinct hours it read in, how many of the 24 hours of a day they fall in, and the median gap in hours between them, 0 with fewer than two. |
| read\_hours\_of\_day | [ uint32](#uint32) | none |
| median\_read\_gap\_hours | [ double](#double) | none |
| load\_share | [ double](#double) | The share of its queries that load files or move them on a stage, from 0 to 1. |
### BatchSqlDetailsRequest
(synq.entities.sql\_insights.v1.BatchSqlDetailsRequest)
BatchSqlDetailsRequest requests SQL details for a set of entities.
| Field | Type | Description |
| - | - | - |
| paths | [repeated string](#string) | The synq paths of the entities to fetch SQL details for. A synq path uniquely identifies an entity and can be obtained from the `synq_paths` field of other entity responses. |
| include\_joins | [ bool](#bool) | Set to return each entity's joins in `SqlDetails.joins`. Off by default: most callers want only the constructs, and an entity whose SQL joins many tables carries every column it joins on. |
### BatchSqlDetailsResponse
(synq.entities.sql\_insights.v1.BatchSqlDetailsResponse)
BatchSqlDetailsResponse maps each requested synq path to its SQL details.
| Field | Type | Description |
| - | - | - |
| sql\_details | [map SqlDetailsEntry](#sqldetailsentry) | SQL details keyed by synq path, exactly as requested. Every requested path is present; a path with no SQL details has an empty set of constructs and no joins. |
### BatchSqlDetailsResponse.SqlDetailsEntry
(synq.entities.sql\_insights.v1.BatchSqlDetailsResponse.SqlDetailsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ SqlDetails](#sqldetails) | none |
### Join
(synq.entities.sql\_insights.v1.Join)
Join is one join in a SQL statement, like
`LEFT JOIN customers c ON o.customer_id = c.id AND c.status = 'active'`.
| Field | Type | Description |
| - | - | - |
| source\_entity\_id | [ string](#string) | The entity whose SQL the join was read from: the requested entity, or one that is the same object. `statement_index`, `join_index` and each condition's `location` address that entity's SQL, which can differ from the requested entity's. A join that both a model's or a node's SQL and the warehouse's definition of what it builds make is read from the model or the node, as a warehouse reports its own wrapping around the SQL a model runs. It is the same opaque identifier the request's `paths` take. |
| kind | [ JoinKind](#joinkind) | The kind of join, like INNER or LEFT\_OUTER, as the SQL wrote it. Which table it keeps every row of follows the order of the tables in the FROM clause, not the order of a condition's `left` and `right`. |
| conditions | [repeated JoinCondition](#joincondition) | The comparisons the join matches rows on, in the order the SQL wrote them, like `o.customer_id = c.id`. More than one is a composite key: a row matches when every condition holds. Never empty. |
| filters | [repeated string](#string) | The comparisons of the same ON clause between a column and a literal, as SQL, like `c.status = 'active'`. They make the join conditional. Empty for a join written with USING, NATURAL or in a WHERE clause. |
| statement\_index | [ uint32](#uint32) | Index of the statement the join was read from, among the SQL statements of `source_entity_id`, counting from 0. |
| join\_index | [ uint32](#uint32) | Index of the join within its statement, counting from 0. Together with `source_entity_id` and `statement_index` it identifies the join. |
### JoinColumn
(synq.entities.sql\_insights.v1.JoinColumn)
JoinColumn is one side of a join condition: a column of a known entity, as
the SQL reads it.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The entity the column belongs to. It is the same opaque identifier the request's `paths` take, so it can be passed back to look that entity up. |
| column | [ string](#string) | The column name, lowercased. |
| derivation | [ JoinSideDerivation](#joinsidederivation) | How the column reaches the comparison. |
| expression | [ string](#string) | The side's SQL when `derivation` is TRANSFORMED, like `lower(c.email)`. For an OTHER operator, the whole comparison. Empty otherwise. |
### JoinCondition
(synq.entities.sql\_insights.v1.JoinCondition)
JoinCondition is one comparison a join matches rows on: a column of one table
compared to a column of another.
| Field | Type | Description |
| - | - | - |
| left | [ JoinColumn](#joincolumn) | The operand the SQL wrote first. For an UPDATE, DELETE or MERGE it is the table the statement writes to. |
| right | [ JoinColumn](#joincolumn) | The operand the SQL wrote second. |
| operator | [ JoinOperator](#joinoperator) | The comparison made between the two sides. |
| location | [ SqlLocation](#sqllocation) | Where the comparison is in the SQL of the join's `source_entity_id`: the text `synq.entities.code.v1.CodeService.ListCode` returns for that entity under `CODE_SOURCE_SQL_DEFINITION`. Unset when the parser recorded no position. |
### JoinIssuer
(synq.entities.sql\_insights.v1.JoinIssuer)
JoinIssuer is one tool, one entity of a tool, or one kind of warehouse
user with no tool, that ran queries making a join. An issuer entity, like a
dbt model or a Looker dashboard, has the role that the entity of a SQL
definition has in `sources`.
| Field | Type | Description |
| - | - | - |
| platform | [ string](#string) | The tool that ran the queries, from the query's tag, as the lowercase platform name, like `looker` or `dbt_cloud`. Empty when no tag names a tool, and `kind` then says who ran them. |
| entity\_id | [ string](#string) | The entity that ran the queries, when the tag names one. Empty otherwise. It is the same opaque identifier the request's `entity_id` takes. |
| queries | [ uint64](#uint64) | The queries it ran that made the join. |
| distinct\_logins | [ uint32](#uint32) | The distinct warehouse users that ran them. |
| kind | [ JoinIssuerKind](#joinissuerkind) | Who ran the queries. Unspecified only from a server that does not classify them yet. |
### JoinKeyPart
(synq.entities.sql\_insights.v1.JoinKeyPart)
JoinKeyPart is one comparison of a join key, read from the side of the
requested entity: `column` belongs to the requested entity, or to an entity
that is the same object, and `other` to the entity it is joined with.
| Field | Type | Description |
| - | - | - |
| column | [ JoinColumn](#joincolumn) | The requested entity's side of the comparison. |
| other | [ JoinColumn](#joincolumn) | The side it is compared with. |
| kind | [ JoinKind](#joinkind) | The kind of join, seen from `column`'s side. A join the SQL writes as `other RIGHT JOIN column` reads as LEFT\_OUTER here, as it keeps every row of `column`'s table. It is the kind of the pattern's first join type, and `JoinPattern.join_types` lists every one. |
| operator | [ JoinOperator](#joinoperator) | The comparison, read as `column other`. |
| leads | [ bool](#bool) | Set for the side with a role of its own in an ASOF, UPDATE\_FROM, DELETE\_USING or MERGE join: the side whose rows look for a match in an ASOF join, or the table the statement writes to. |
| join\_type | [ JoinType](#jointype) | The join type, seen from `column`'s side, as a semantic layer names it. OTHER for a kind with no join type of its own, and `native_join_type` then names it. |
| native\_join\_type | [ string](#string) | The kind's own name when `join_type` is OTHER, like `RIGHT_OUTER` or `ASOF`. Empty otherwise. |
### JoinPattern
(synq.entities.sql\_insights.v1.JoinPattern)
JoinPattern is one way to join the requested entity: a join key, and the
SQL that joins on it. The same key written in several SQL definitions, or
joined with several join types, is one pattern listing each of them.
| Field | Type | Description |
| - | - | - |
| key | [repeated JoinKeyPart](#joinkeypart) | The comparisons of the key, read from the requested entity's side and ordered by its column. More than one is a composite key: a row matches when every comparison holds. Never empty. |
| filters | [repeated string](#string) | The comparisons between a column and a literal that the same ON clauses add, as SQL, like `c.status = 'active'`. They make the join conditional. Each is listed once, in the order first seen. |
| evidence\_kinds | [repeated JoinEvidenceKind](#joinevidencekind) | Where the pattern was seen. |
| sources | [repeated JoinPatternSource](#joinpatternsource) | Each join, in the SQL of a table, view or model in the workspace, that joins on this key. Ordered by `entity_id`. Entities that are the same object are listed once: a join both a dbt model and the view it builds make is listed from the model, as `BatchSqlDetails` reads it. |
| query\_log | [ JoinQueryLogEvidence](#joinquerylogevidence) | How queries in the query history made the join in the requested window, over every join type in `join_types`. A warehouse user is counted once. Set when `evidence_kinds` holds QUERY\_LOG. |
| join\_types | [repeated JoinPatternType](#joinpatterntype) | Each join type the key is joined with, the most used first: by the SQL definitions that use it, then by queries. The `kind`, `join_type` and `native_join_type` of the key's comparisons are those of the first. |
### JoinPatternSource
(synq.entities.sql\_insights.v1.JoinPatternSource)
JoinPatternSource is one join in the SQL of one entity that joins on the
pattern's key.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The entity whose SQL makes the join. It is the same opaque identifier the request's `entity_id` takes. |
| statement\_index | [ uint32](#uint32) | Index of the statement the join is in, among the SQL statements of `entity_id`, counting from 0. |
| join\_index | [ uint32](#uint32) | Index of the join within its statement, counting from 0. It is the `join_index` of the same join in `BatchSqlDetails` for `entity_id`. |
### JoinPatternType
(synq.entities.sql\_insights.v1.JoinPatternType)
JoinPatternType is one join type that a pattern's key is joined with, and
the evidence of that join type only.
| Field | Type | Description |
| - | - | - |
| kind | [ JoinKind](#joinkind) | The kind of join, seen from the requested entity's side, as `JoinKeyPart.kind` is. |
| join\_type | [ JoinType](#jointype) | The join type, seen from the requested entity's side, as a semantic layer names it. |
| native\_join\_type | [ string](#string) | The kind's own name when `join_type` is OTHER, like `RIGHT_OUTER`. Empty otherwise. |
| keeps | [ JoinKeeps](#joinkeeps) | Which side keeps the rows that find no match on the other side. |
| filters | [repeated string](#string) | The comparisons between a column and a literal that the ON clauses of this join type add, as SQL. Each is listed once, in the order first seen. |
| sources | [repeated JoinPatternSource](#joinpatternsource) | The joins of the pattern's `sources` that use this join type, in the same order. |
| query\_log | [ JoinQueryLogEvidence](#joinquerylogevidence) | How queries in the query history made the join with this join type. Set when queries made it. |
### JoinQueryLogEvidence
(synq.entities.sql\_insights.v1.JoinQueryLogEvidence)
JoinQueryLogEvidence sums the queries that made a join in the requested
window. A query counts once per join, also for a composite key.
| Field | Type | Description |
| - | - | - |
| queries | [ uint64](#uint64) | The queries that made the join. |
| distinct\_logins | [ uint32](#uint32) | The distinct warehouse users that ran them. A query that names no user adds none. |
| first\_hour | [ Timestamp](#timestamp) | The first and the last hour the join was seen in, each the start of its UTC hour. |
| last\_hour | [ Timestamp](#timestamp) | none |
| issuers | [repeated JoinIssuer](#joinissuer) | Who ran the queries, most queries first. |
| sample\_query\_id | [ string](#string) | The warehouse's id of a query of the last hour that made the join. |
### JoinQueryLogLeftOut
(synq.entities.sql\_insights.v1.JoinQueryLogLeftOut)
JoinQueryLogLeftOut counts the queries left out of the query history
evidence, of the joins that pass every other filter of the request. A
query counts once, however many patterns its join makes. The thresholds
never count them.
| Field | Type | Description |
| - | - | - |
| coalesce\_quality\_queries | [ uint64](#uint64) | Queries that Coalesce Quality's tag marks, by any warehouse user. |
| coalesce\_logins | [ uint64](#uint64) | The other queries of the warehouse users Coalesce connects as, for Coalesce Quality or Coalesce Catalog. |
| service\_accounts | [ uint64](#uint64) | Queries of the warehouse users that `exclude_service_accounts` leaves out. |
### JoinQueryLogRequest
(synq.entities.sql\_insights.v1.JoinQueryLogRequest)
JoinQueryLogRequest says which hours of query history to read joins from,
and which joins seen only there to leave out.
| Field | Type | Description |
| - | - | - |
| from | [ Timestamp](#timestamp) | The window, from `from` up to but not including `to`. Each end is taken at the start of its UTC hour. It spans at most 92 days. |
| to | [ Timestamp](#timestamp) | none |
| min\_queries | [ uint64](#uint64) | A pattern seen only in the query history is listed when it reaches any of these thresholds that is set. 0 sets none, and with none set every pattern is listed. A pattern that a SQL definition makes is always listed. |
| min\_logins | [ uint32](#uint32) | none |
| include\_coalesce\_quality | [ bool](#bool) | Set to keep the queries Coalesce ran: those Coalesce Quality's tag marks, and those of the warehouse users Coalesce connects as. Leave unset to leave them out, as popularity does, and count them in `query_log_left_out`. |
| exclude\_service\_accounts | [ bool](#bool) | Set to leave out the queries of every warehouse user in a service account, and count them in `query_log_left_out`. |
### ListJoinPatternsRequest
(synq.entities.sql\_insights.v1.ListJoinPatternsRequest)
ListJoinPatternsRequest names the table to list join patterns for.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The synq path of the table, view or model to list the join patterns of, as the `paths` of `BatchSqlDetailsRequest` take it. |
| column | [ string](#string) | Set to return only the patterns whose key compares this column of the entity, like `customer_id`. Matched case-insensitively. Leave empty for every column. |
| other\_entity\_id | [ string](#string) | Set to return only how the entity is joined with this other entity, which answers "how do I join these two tables". A key that also compares a third table keeps only its comparisons with this one. Like `entity_id`, it matches every entity that is the same object. Leave empty for joins with any entity. |
| query\_log | [ JoinQueryLogRequest](#joinquerylogrequest) | Set to also list the joins that queries in the warehouse's query history made in a window, with QUERY\_LOG evidence. Leave unset for the joins that SQL definitions make only. |
| bare\_only | [ bool](#bool) | Set to return only the patterns whose every side is a plain column, with no expression around it, like `lower(email)` or a cast. |
### ListJoinPatternsResponse
(synq.entities.sql\_insights.v1.ListJoinPatternsResponse)
ListJoinPatternsResponse lists the join patterns of the requested entity.
| Field | Type | Description |
| - | - | - |
| patterns | [repeated JoinPattern](#joinpattern) | The join patterns. Those that a SQL definition makes come first, then those of the query history only, by distinct logins, the last hour seen and queries. Empty when no SQL in the workspace joins the entity, or none was parsed yet. |
| query\_log\_covered\_hours | [ uint32](#uint32) | How many hours of the `query_log` window the query history covered in full. 0 with no QUERY\_LOG pattern means that no query history was read for the window, not that no query made a join. |
| query\_log\_left\_out | [ JoinQueryLogLeftOut](#joinquerylogleftout) | The queries left out of the query history evidence. Set when the request sets `query_log`. |
| entity\_ids | [repeated string](#string) | The requested entity first, then every entity that is the same object, like the table a dbt model builds. The patterns already include the joins of each of them, so asking about another one of these returns the same patterns. |
### ListSqlConstructsRequest
(synq.entities.sql\_insights.v1.ListSqlConstructsRequest)
ListSqlConstructsRequest has no fields: the result is scoped to the workspace
of the authenticated request.
### ListSqlConstructsResponse
(synq.entities.sql\_insights.v1.ListSqlConstructsResponse)
ListSqlConstructsResponse reports how many of the workspace's entities use
each SQL construct.
| Field | Type | Description |
| - | - | - |
| sql\_constructs | [repeated SqlConstructCount](#sqlconstructcount) | One entry per construct that appears at least once in the workspace. |
### ListSqlConstructsResponse.SqlConstructCount
(synq.entities.sql\_insights.v1.ListSqlConstructsResponse.SqlConstructCount)
SqlConstructCount pairs a SQL construct with the number of entities in the
workspace whose SQL uses it.
| Field | Type | Description |
| - | - | - |
| sql\_construct | [ SqlConstruct](#sqlconstruct) | The SQL construct being counted. |
| count | [ int32](#int32) | How many entities in the workspace use this construct. |
### SqlDetails
(synq.entities.sql\_insights.v1.SqlDetails)
SqlDetails describes the SQL used by a single entity.
| Field | Type | Description |
| - | - | - |
| path | [ string](#string) | The synq path of the entity these details belong to. |
| sql\_constructs | [repeated SqlConstruct](#sqlconstruct) | The set of SQL constructs detected in the entity's SQL. Multiple constructs can be present on a single entity (e.g. a query with both a CTE and an INNER JOIN reports HAS\_CTE, HAS\_ANY\_JOIN and INNER\_JOIN). |
| joins | [repeated Join](#join) | How building this entity joins other tables: every join whose key compares a column of one known entity with a column of another. A join is left out when none of its key's comparisons can be tied to one column of a known entity on both sides, and a comparison that cannot is left out of its join's `conditions`. |
The joins come from the entity's own SQL and from the SQL of every entity that is the same object, like a dbt model, the table it builds and that table in a catalog. Each of them is answered with the same list. A join found in more than one of those SQLs is listed once: two joins are the same when their conditions compare the same columns with the same operators and the same expressions, a column of the dbt model and the same column of the table it builds counting as one. The join kind does not tell joins apart.
A model's or a node's joins come first, in the order its SQL makes them, then the joins only a warehouse's definition of the same object makes.
Empty unless the request set `include_joins`, and for an entity whose SQL was not parsed yet or joins nothing. |
### SqlLocation
(synq.entities.sql\_insights.v1.SqlLocation)
SqlLocation is a range of SQL text. Lines and columns are 1-based and
inclusive.
| Field | Type | Description |
| - | - | - |
| start\_line | [ int64](#int64) | First line of the range. |
| start\_column | [ int64](#int64) | Column of the first character on the first line. |
| end\_line | [ int64](#int64) | Last line of the range. |
| end\_column | [ int64](#int64) | Column of the last character on the last line. |
### BatchGetEntitiesRequest
(synq.entities.entities.v1.BatchGetEntitiesRequest)
BatchGetEntitiesRequest is the request message for the BatchGetEntities method.
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | Identifiers of the entities to get. |
### BatchGetEntitiesResponse
(synq.entities.entities.v1.BatchGetEntitiesResponse)
BatchGetEntitiesResponse is the response message for the BatchGetEntities method.
| Field | Type | Description |
| - | - | - |
| entities | [repeated Entity](#entity) | The entities that were retrieved. |
### GetEntityRequest
(synq.entities.entities.v1.GetEntityRequest)
GetEntityRequest is the request message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to get. |
### GetEntityResponse
(synq.entities.entities.v1.GetEntityResponse)
GetEntityResponse is the response message for the GetEntity method.
| Field | Type | Description |
| - | - | - |
| entity | [ Entity](#entity) | The entity that was retrieved. |
### ListEntitiesRequest
(synq.entities.entities.v1.ListEntitiesRequest)
| Field | Type | Description |
| - | - | - |
| query | [ Query](#query) | none |
| pagination | [ Pagination](#pagination) | none |
### ListEntitiesResponse
(synq.entities.entities.v1.ListEntitiesResponse)
| Field | Type | Description |
| - | - | - |
| page\_info | [ PageInfo](#pageinfo) | none |
| entity\_ids | [repeated Identifier](#identifier) | none |
### SearchEntitiesRequest
(synq.entities.entities.v1.SearchEntitiesRequest)
| Field | Type | Description |
| - | - | - |
| query | [ string](#string) | Full text search query, it will search in name but also in description and columns of the entities |
| limit\_to\_types | [repeated EntityType](#entitytype) | Limit results to specific entity types, leave empty to use only the query |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_page.page | [optional uint32](#uint32) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_limit\_per\_page.limit\_per\_page | [optional uint32](#uint32) | none |
### SearchEntitiesResponse
(synq.entities.entities.v1.SearchEntitiesResponse)
| Field | Type | Description |
| - | - | - |
| hits | [repeated SearchHit](#searchhit) | The entities that were retrieved. |
| estimated\_total\_hits | [ uint64](#uint64) | Estimated number of all search results |
| entity\_type\_estimates | [repeated EntityTypeSearchFacet](#entitytypesearchfacet) | Estimated number of all search results by entity type |
### EntityTypeSearchFacet
(synq.entities.entities.v1.EntityTypeSearchFacet)
| Field | Type | Description |
| - | - | - |
| entity\_type | [ EntityType](#entitytype) | none |
| count | [ uint32](#uint32) | none |
### SearchHit
(synq.entities.entities.v1.SearchHit)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the found entity with optional folder like path |
| description | [ string](#string) | Description is used for display and search |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_entity\_id.entity\_id | [optional Identifier](#identifier) | Identifier of the entity |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_synq\_path.synq\_path | [optional string](#string) | Coalesce Quality path, as used in frontend |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_entity\_type.entity\_type | [optional EntityType](#entitytype) | Information about entity type |
| ranking\_score | [ float](#float) | Ranking score, higher is better |
### BatchResolveIdentifiersRequest
(synq.entities.resolve.v1.BatchResolveIdentifiersRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | Identifiers to resolve. |
| check\_existence | [ bool](#bool) | When true, also verify that the resolved entities actually exist in the system. |
### BatchResolveIdentifiersResponse
(synq.entities.resolve.v1.BatchResolveIdentifiersResponse)
| Field | Type | Description |
| - | - | - |
| resolved\_identifiers | [repeated ResolvedIdentifier](#resolvedidentifier) | none |
### ResolvedIdentifier
(synq.entities.resolve.v1.ResolvedIdentifier)
ResolvedIdentifier contains the resolution result for a single input identifier.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | The original input identifier. |
| synq\_path | [ string](#string) | The Coalesce Quality path for this identifier. |
| identities | [repeated Identifier](#identifier) | All identifiers that are part of the same identity group (including the input identifier). |
| identity\_synq\_paths | [repeated string](#string) | All Coalesce Quality paths in the identity group. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_exists.exists | [optional bool](#bool) | Whether the entity exists in the system. Only populated when check\_existence is true in the request. |
### ListCodeRequest
(synq.entities.code.v1.ListCodeRequest)
ListCodeRequest is the request message for the ListCode method.
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | Identifier of the entity to retrieve code for. This can be any entity type that contains code artifacts. |
### ListCodeResponse
(synq.entities.code.v1.ListCodeResponse)
ListCodeResponse is the response message for the ListCode method.
| Field | Type | Description |
| - | - | - |
| codes | [repeated Code](#code) | List of code artifacts associated with the requested entity. Each code artifact includes its content, type, and metadata about when it was captured. |
| identifiers | [repeated Identifier](#identifier) | none |
| synq\_paths | [repeated string](#string) | none |
| unavailable\_sources | [repeated CodeSourceError](#codesourceerror) | Sources that could not be read while assembling this response. Empty on a complete response, which is the normal case. |
Code is composed from several independent sources, so one of them being briefly unavailable degrades the response rather than failing it: the codes that were read are returned, and the source that was not is named here. Retry to pick up what is missing.
An empty `codes` always means the entity has no code, never that a source was unavailable — a request that could read nothing at all returns an error rather than an empty response, because the two are otherwise indistinguishable. |
### ListEntityTraitDefsRequest
(synq.entities.typedefs.v1.ListEntityTraitDefsRequest)
### ListEntityTraitDefsResponse
(synq.entities.typedefs.v1.ListEntityTraitDefsResponse)
| Field | Type | Description |
| - | - | - |
| entity\_trait\_defs | [repeated EntityTraitDef](#entitytraitdef) | Every trait a selection query can filter by, ordered by name. |
### ListEntityTypeDefsRequest
(synq.entities.typedefs.v1.ListEntityTypeDefsRequest)
### ListEntityTypeDefsResponse
(synq.entities.typedefs.v1.ListEntityTypeDefsResponse)
| Field | Type | Description |
| - | - | - |
| entity\_type\_defs | [repeated EntityTypeDef](#entitytypedef) | Every entity type in your workspace, built-in and your own, ordered by entity type value. Your own types sort together after the built-in ones, and two calls against an unchanged workspace return the same order. |
### ListExecutionTypeDefsRequest
(synq.entities.typedefs.v1.ListExecutionTypeDefsRequest)
### ListExecutionTypeDefsResponse
(synq.entities.typedefs.v1.ListExecutionTypeDefsResponse)
| Field | Type | Description |
| - | - | - |
| execution\_type\_defs | [repeated ExecutionTypeDef](#executiontypedef) | Every kind of run, ordered by execution type value. |
### ListPlatformDefsRequest
(synq.entities.typedefs.v1.ListPlatformDefsRequest)
### ListPlatformDefsResponse
(synq.entities.typedefs.v1.ListPlatformDefsResponse)
| Field | Type | Description |
| - | - | - |
| platform\_defs | [repeated PlatformDef](#platformdef) | Every platform, ordered by platform value. |
### EntityTraitDef
(synq.entities.typedefs.v1.EntityTraitDef)
EntityTraitDef describes one trait a selection query can filter entities by.
A trait names a property a type has rather than a type itself, so a selection can
ask for "everything a monitor can run on" instead of listing the forty-odd types
that satisfy it today. The set of types it stands for is worked out each time the
query runs, so a selection saved this way covers a new platform's types as soon
as Coalesce Quality supports them, and you never have to revisit it.
In a query written as text this is `with_type(traits="is_monitorable")`; in a
structured query it is the `traits` field on `synq.queries.v1.WithType`.
Read this list rather than keeping your own. Traits are added as the platform
grows, and a name that is not in the list selects NOTHING — so a stale copy
narrows a selection to empty rather than failing where you would notice.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | The trait's name, and the exact string a query carries: `is_monitorable`. Stable and safe to store. Matching ignores case. |
| short\_name | [ string](#string) | The trait's name for display: "Monitorable", "Warehouse table". |
| description | [ string](#string) | A sentence explaining what the trait means and what selecting on it gets you. |
| entity\_types | [repeated EntityType](#entitytype) | Every entity type that carries this trait today. |
It is this server's current answer, not a definition — the point of selecting by trait is that this set grows without your query changing. Use it to show or preview what a selection covers, never to expand a trait into types and store that instead. |
### EntityTypeDef
(synq.entities.typedefs.v1.EntityTypeDef)
EntityTypeDef describes one kind of entity that exists in your workspace — a
BigQuery table, a dbt model, an Airflow DAG, or a type you defined yourself.
Use it to turn the `entity_type` that other endpoints return into something you
can show a person or make a decision about: a name to display, the platform it
came from, and what Coalesce Quality does with it.
The built-in types and your own custom types both come back here, in this same
shape. A custom type is the one with a non-zero `type_id`.
| Field | Type | Description |
| - | - | - |
| entity\_type | [ EntityType](#entitytype) | The entity type, matching the `entity_type` other endpoints return for an entity of this kind. Compare against it directly. |
If your generated code is older than the server, a type it does not know arrives as an unrecognised number — `name` still identifies it. |
\| name | [ string](#string) | A stable name for the type that never changes, safe to store and to compare against. Built-in types use the enum spelling, e.g. `ENTITY_TYPE_BQ_TABLE`. Your own types are named `CUSTOM_TYPE_`, e.g. `CUSTOM_TYPE_20`, which stays the same even if you rename the type.
Prefer this over the numeric value whenever you store a reference to a type.
It is also the name a selection query spells the type by, lowercased and without the `ENTITY_TYPE_` prefix: `with_type("bq_table")` for a built-in, `with_type("custom_type_20")` for one of your own. Matching there ignores case, so this value can be passed through as it is. |
\| short\_name | [ string](#string) | The type's name without its platform: "Table", "View", "DAG". Use it where the platform is already clear from the surrounding context.
For a type you defined yourself, this is the name you gave it. |
\| short\_plural\_name | [ string](#string) | The plural of `short_name`, for counts and headings: "Tables", "Views".
Empty for a type you defined yourself, since you gave it a single name — fall back to `short_name`. |
\| full\_name | [ string](#string) | The type's name including its platform: "BigQuery Table", "Snowflake View". Use it where a type appears on its own, so that a table from two different warehouses still reads clearly.
For a type you defined yourself, this is the same as `short_name`. |
\| full\_plural\_name | [ string](#string) | The plural of `full_name`: "BigQuery Tables". Empty under the same condition as `short_plural_name`. |
\| description | [ string](#string) | A sentence explaining the type, where there is one. Often empty. |
\| platform | [ DataPlatformType](#dataplatformtype) | The platform this type belongs to — the warehouse, BI tool or orchestrator it comes from. Pair it with ListPlatformDefs to show that platform's name.
A type you defined yourself belongs to no external platform, so it reports Coalesce Quality itself. |
\| traits | [ Traits](#traits) | What Coalesce Quality knows about entities of this type.
For a built-in type this is everything it knows. For a type you defined yourself it is the traits that can be declared on a custom type, each reported whether you set it or not; the traits Coalesce Quality works out on its own for built-in types are not reported for a custom type, so read those with a default. |
\| type\_id | [ int32](#int32) | The id of a type you defined yourself, and 0 for a built-in type. It is the same `type_id` that appears on your entities of this type, and the id you pass to synq.entities.custom.v1.TypesService to change the type or fetch its icon.
A non-zero value is how you tell your own types from the built-in ones. |
### ExecutionTypeDef
(synq.entities.typedefs.v1.ExecutionTypeDef)
ExecutionTypeDef describes one kind of run Coalesce Quality records against an
entity — a dbt invocation, a warehouse query job, a detected schema change.
Use it to name the `execution_type` that execution and run endpoints return.
| Field | Type | Description |
| - | - | - |
| execution\_type | [ ExecutionType](#executiontype) | The execution type, matching the `execution_type` other endpoints return. |
| name | [ string](#string) | A stable name for the execution type that never changes, safe to store: the enum spelling, e.g. `EXECUTION_TYPE_DBT_INVOCATION`. |
| short\_name | [ string](#string) | The execution type's name for display: "Query Job", "Schema Change", "dbt". |
| platform | [ DataPlatformType](#dataplatformtype) | The platform this kind of run comes from. Unset for the kinds Coalesce Quality produces itself, such as a detected schema change. |
| traits | [ Traits](#traits) | What Coalesce Quality knows about this kind of run. Empty for a kind that has no traits of its own. |
### PlatformDef
(synq.entities.typedefs.v1.PlatformDef)
PlatformDef describes one platform Coalesce Quality connects to — a warehouse, a
BI tool, an orchestrator, a transformation framework.
Use it to show a platform's name rather than building one out of an enum value.
| Field | Type | Description |
| - | - | - |
| platform | [ DataPlatformType](#dataplatformtype) | The platform, matching the `platform` on an EntityTypeDef and the platform fields other endpoints return. |
| name | [ string](#string) | A stable name for the platform that never changes, safe to store: the enum spelling, e.g. `DATA_PLATFORM_TYPE_BIGQUERY`. |
| display\_name | [ string](#string) | The platform's name as it is normally written: "BigQuery", "ClickHouse", "Microsoft SQL Server", "dbt". Use this anywhere a person will read it. |
| traits | [ Traits](#traits) | What Coalesce Quality knows about the platform. `is_dwh` tells you whether it is a data warehouse — true for BigQuery and Snowflake, false for dbt or Looker. |
### Traits
(synq.entities.typedefs.v1.Traits)
Traits are the facts Coalesce Quality knows about a kind of thing, beyond its
name — whether it is a transformation model, whether it lives in a warehouse,
whether you can put a monitor on it.
They come back as maps rather than as named fields so that new traits reach you
without regenerating your client. Look up the ones you care about by name and
ignore the rest:
if (def.traits.flags\["is\_model"]) \{ ... }
Common flag names are `is_model`, `is_source`, `is_dwh_table`, `is_dwh_view`,
`is_bi_like`, `is_monitorable` and `is_searchable`. More are added over time, so
treat the map as open-ended.
A name missing from the map has no answer for this type. Read traits with a
default rather than assuming false, so your code keeps working against both an
older and a newer server:
isModel = def.traits.flags.get("is\_model", false)
| Field | Type | Description |
| - | - | - |
| flags | [map FlagsEntry](#flagsentry) | Yes/no traits, by name. For example `{"is_model": true, "is_dwh_table": false}`. |
| strings | [map StringsEntry](#stringsentry) | Traits whose value is a piece of text, by name. For example `{"synql_alias": "table"}`. There are far fewer of these than flags, and most types have none. |
### Traits.FlagsEntry
(synq.entities.typedefs.v1.Traits.FlagsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ bool](#bool) | none |
### Traits.StringsEntry
(synq.entities.typedefs.v1.Traits.StringsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### BatchGetExecutionsRequest
(synq.entities.executions.v2.BatchGetExecutionsRequest)
BatchGetExecutionsRequest requests specific executions by ID.
| Field | Type | Description |
| - | - | - |
| execution\_ids | [repeated string](#string) | Execution IDs to retrieve. |
### BatchGetExecutionsResponse
(synq.entities.executions.v2.BatchGetExecutionsResponse)
BatchGetExecutionsResponse contains the requested executions.
| Field | Type | Description |
| - | - | - |
| executions | [map ExecutionsEntry](#executionsentry) | Map of execution ID to EntityExecution. Only includes executions that were found and accessible. |
### BatchGetExecutionsResponse.ExecutionsEntry
(synq.entities.executions.v2.BatchGetExecutionsResponse.ExecutionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ EntityExecution](#entityexecution) | none |
### EntityExecution
(synq.entities.executions.v2.EntityExecution)
EntityExecution represents a single execution event on an entity.
This is a public representation of internal Run data, excluding sensitive metadata.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Unique identifier for this execution. |
| entities | [repeated Identifier](#identifier) | Primary entities affected by this execution. These are the main entities this execution ran on or modified. |
| entities\_synq\_paths | [repeated string](#string) | Coalesce Quality paths of primary entities affected by this execution. |
| targets | [repeated Identifier](#identifier) | Target entities referenced by this execution. These are secondary entities that were targets of the execution. |
| targets\_synq\_paths | [repeated string](#string) | Coalesce Quality paths of target entities referenced by this execution. |
| extra\_references | [repeated Identifier](#identifier) | Additional entity references. These are other entities referenced during the execution. |
| extra\_references\_synq\_paths | [repeated string](#string) | Coalesce Quality paths of additional entity references. |
| execution\_type | [ ExecutionType](#executiontype) | Type of execution (e.g., query, job, test run). |
| status | [ ExecutionStatus](#executionstatus) | Status of the execution. |
| skipped | [ bool](#bool) | Whether this execution was skipped. |
| message | [ string](#string) | Human-readable message describing the execution result. This typically contains error messages, warnings, or success information. |
| created\_at | [ Timestamp](#timestamp) | When the execution record was created in Coalesce Quality. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_started\_at.started\_at | [optional Timestamp](#timestamp) | When the execution actually started. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_finished\_at.finished\_at | [optional Timestamp](#timestamp) | When the execution finished. |
| parent\_execution\_ids | [repeated string](#string) | IDs of parent executions, if this is a nested execution. For example, a task execution might have a parent DAG execution. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_duration\_seconds.duration\_seconds | [optional double](#double) | Duration of the execution in seconds. Only populated if both started\_at and finished\_at are present. |
### ExecutionStatusCount
(synq.entities.executions.v2.ExecutionStatusCount)
ExecutionStatusCount represents the count of executions by status.
| Field | Type | Description |
| - | - | - |
| status | [ ExecutionStatus](#executionstatus) | none |
| count | [ int64](#int64) | none |
### ExecutionTypeCount
(synq.entities.executions.v2.ExecutionTypeCount)
ExecutionTypeCount represents the count of executions by type.
| Field | Type | Description |
| - | - | - |
| execution\_type | [ ExecutionType](#executiontype) | none |
| count | [ int64](#int64) | none |
### GetExecutionsSummaryRequest
(synq.entities.executions.v2.GetExecutionsSummaryRequest)
GetExecutionsSummaryRequest requests summary statistics for entities.
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated Identifier](#identifier) | Entity identifiers to get summary for. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_started\_after.execution\_started\_after | [optional Timestamp](#timestamp) | Return executions that started after this timestamp. If not provided, defaults to 30 days before execution\_started\_before. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_started\_before.execution\_started\_before | [optional Timestamp](#timestamp) | Return executions that started before this timestamp. If not provided, defaults to now(). |
### GetExecutionsSummaryResponse
(synq.entities.executions.v2.GetExecutionsSummaryResponse)
GetExecutionsSummaryResponse contains aggregated execution statistics.
| Field | Type | Description |
| - | - | - |
| total\_executions | [ int64](#int64) | Total number of executions matching the criteria. |
| execution\_type\_counts | [repeated ExecutionTypeCount](#executiontypecount) | Count of executions by type. |
| status\_counts | [repeated ExecutionStatusCount](#executionstatuscount) | Count of executions by status. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_earliest\_execution.earliest\_execution | [optional Timestamp](#timestamp) | Time range of available executions. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_latest\_execution.latest\_execution | [optional Timestamp](#timestamp) | none |
| latest\_by\_entity | [map LatestByEntityEntry](#latestbyentityentry) | Latest execution for each requested entity. |
### GetExecutionsSummaryResponse.LatestByEntityEntry
(synq.entities.executions.v2.GetExecutionsSummaryResponse.LatestByEntityEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ EntityExecution](#entityexecution) | none |
### GetLatestExecutionsRequest
(synq.entities.executions.v2.GetLatestExecutionsRequest)
GetLatestExecutionsRequest requests the latest execution for each entity.
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated Identifier](#identifier) | Entity identifiers to get latest executions for. Only returns executions where these entities appear in EntityExecution.entities (what executed). Examples that WILL return results: monitors, dbt models, airflow tasks, tests Examples that WON'T return results: tables (they don't execute, they are created/updated by models) |
| statuses | [repeated ExecutionStatus](#executionstatus) | Filter by execution status. If provided, returns the latest execution with one of these statuses. If empty, returns the latest execution regardless of status. |
| execution\_types | [repeated ExecutionType](#executiontype) | Filter by execution types. If provided, returns the latest execution of one of these types. If empty, returns the latest execution regardless of type. |
### GetLatestExecutionsResponse
(synq.entities.executions.v2.GetLatestExecutionsResponse)
GetLatestExecutionsResponse contains the latest execution for each requested entity.
| Field | Type | Description |
| - | - | - |
| latest\_executions | [map LatestExecutionsEntry](#latestexecutionsentry) | Map of entity identifier (as string) to latest execution. Entities with no matching executions are omitted. |
### GetLatestExecutionsResponse.LatestExecutionsEntry
(synq.entities.executions.v2.GetLatestExecutionsResponse.LatestExecutionsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ EntityExecution](#entityexecution) | none |
### ListExecutionsRequest
(synq.entities.executions.v2.ListExecutionsRequest)
ListExecutionsRequest specifies filters for listing executions.
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated Identifier](#identifier) | Entity identifiers to get executions for. Executions affecting any of these entities will be returned. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_started\_after.execution\_started\_after | [optional Timestamp](#timestamp) | Return executions that started after this timestamp. If not provided, defaults to 7 days before execution\_started\_before. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_execution\_started\_before.execution\_started\_before | [optional Timestamp](#timestamp) | Return executions that started before this timestamp. If not provided, defaults to now(). |
| execution\_types | [repeated ExecutionType](#executiontype) | Filter by specific execution types. If empty, all execution types are included. |
| statuses | [repeated ExecutionStatus](#executionstatus) | Filter by execution status. If empty, all statuses are included. |
| include\_referenced\_executions | [ bool](#bool) | Include executions that reference the specified entities (not just primary entities). If true, executions where entity\_ids appear in targets or extra\_references are also returned. |
| pagination | [ Pagination](#pagination) | Pagination parameters. |
### ListExecutionsResponse
(synq.entities.executions.v2.ListExecutionsResponse)
ListExecutionsResponse contains the paginated list of executions.
| Field | Type | Description |
| - | - | - |
| executions | [repeated EntityExecution](#entityexecution) | List of executions matching the filter criteria, ordered by started\_at descending. |
| page\_info | [ PageInfo](#pageinfo) | Pagination information. |
### Execution
(synq.entities.executions.v1.Execution)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | none |
| status | [ ExecutionStatus](#executionstatus) | none |
| message | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| finished\_at | [ Timestamp](#timestamp) | none |
| annotations | [repeated Annotation](#annotation) | none |
| extras | [repeated ExecutionExtra](#executionextra) | none |
### ExecutionExtra
(synq.entities.executions.v1.ExecutionExtra)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) extra.executed\_sql | [ string](#string) | none |
### LogEntry
(synq.entities.executions.v1.LogEntry)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | none |
| message | [ string](#string) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| started\_at | [ Timestamp](#timestamp) | none |
| finished\_at | [ Timestamp](#timestamp) | none |
| annotations | [repeated Annotation](#annotation) | none |
### UpsertExecutionRequest
(synq.entities.executions.v1.UpsertExecutionRequest)
| Field | Type | Description |
| - | - | - |
| execution | [ Execution](#execution) | none |
### UpsertExecutionResponse
(synq.entities.executions.v1.UpsertExecutionResponse)
### UpsertLogEntryRequest
(synq.entities.executions.v1.UpsertLogEntryRequest)
| Field | Type | Description |
| - | - | - |
| log\_entry | [ LogEntry](#logentry) | none |
### UpsertLogEntryResponse
(synq.entities.executions.v1.UpsertLogEntryResponse)
### BatchGetFoldNodesRequest
(synq.entities.lineage.folds.v1alpha.BatchGetFoldNodesRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Entity ids of fold nodes, as a lineage response returns them. |
### BatchGetFoldNodesResponse
(synq.entities.lineage.folds.v1alpha.BatchGetFoldNodesResponse)
| Field | Type | Description |
| - | - | - |
| fold\_nodes | [map FoldNodesEntry](#foldnodesentry) | Fold nodes keyed by entity id. |
### BatchGetFoldNodesResponse.FoldNodesEntry
(synq.entities.lineage.folds.v1alpha.BatchGetFoldNodesResponse.FoldNodesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ FoldNode](#foldnode) | none |
### BatchGetFoldsRequest
(synq.entities.lineage.folds.v1alpha.BatchGetFoldsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### BatchGetFoldsResponse
(synq.entities.lineage.folds.v1alpha.BatchGetFoldsResponse)
| Field | Type | Description |
| - | - | - |
| folds | [map FoldsEntry](#foldsentry) | Folds keyed by id. |
### BatchGetFoldsResponse.FoldsEntry
(synq.entities.lineage.folds.v1alpha.BatchGetFoldsResponse.FoldsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ LineageFold](#lineagefold) | none |
### CreateFoldRequest
(synq.entities.lineage.folds.v1alpha.CreateFoldRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the new fold, a UUID. |
| name | [ string](#string) | Name shown on the fold's node. |
| matcher | [ FoldMatcher](#foldmatcher) | Which entities the fold shows as its node. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_enabled.enabled | [optional bool](#bool) | Whether lineage applies the fold from the start. Defaults to on. |
| actor | [ Actor](#actor) | Who is making the change. Optional: the identity is completed from the calling credentials. Set it to attribute the change to a person. |
### CreateFoldResponse
(synq.entities.lineage.folds.v1alpha.CreateFoldResponse)
| Field | Type | Description |
| - | - | - |
| fold | [ LineageFold](#lineagefold) | none |
### DeleteFoldRequest
(synq.entities.lineage.folds.v1alpha.DeleteFoldRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | When set, the delete is refused with ABORTED if the fold changed since this etag was read. |
| actor | [ Actor](#actor) | none |
### DeleteFoldResponse
(synq.entities.lineage.folds.v1alpha.DeleteFoldResponse)
### ListFoldsRequest
(synq.entities.lineage.folds.v1alpha.ListFoldsRequest)
### ListFoldsResponse
(synq.entities.lineage.folds.v1alpha.ListFoldsResponse)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### PreviewFoldRequest
(synq.entities.lineage.folds.v1alpha.PreviewFoldRequest)
| Field | Type | Description |
| - | - | - |
| matcher | [ FoldMatcher](#foldmatcher) | The matcher to preview. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sample\_size.sample\_size | [optional int32](#int32) | How many sample entities to return, at most. Defaults to 20. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_fold\_id.fold\_id | [optional string](#string) | When previewing a change to an existing fold, its id, so the fold is not reported as overlapping with itself. |
### PreviewFoldResponse
(synq.entities.lineage.folds.v1alpha.PreviewFoldResponse)
| Field | Type | Description |
| - | - | - |
| entity\_count | [ int32](#int32) | How many entities in lineage the matcher folds. |
| sample\_entity\_ids | [repeated string](#string) | A sample of the entity ids folded, in id order. |
| edge\_count | [ int32](#int32) | How many lineage edges connect a folded entity to anything. Edges between two folded entities disappear into the fold's node. |
| separated\_entity\_count | [ int32](#int32) | How many folded entities are shown today on one node with other entities that the matcher does not fold, typically the same table as a transformation model or a catalog entry sees it. Folded, such an entity moves to the fold's node and the others stay on a node of their own. |
| sample\_separated\_entities | [repeated SeparatedEntity](#separatedentity) | A sample of them. |
| overlapping\_fold\_ids | [repeated string](#string) | Enabled folds that already match some of the same entities. An entity matched by more than one fold goes to the oldest of them. |
### RenameFoldRequest
(synq.entities.lineage.folds.v1alpha.RenameFoldRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| name | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | When set, the change is refused with ABORTED if the fold changed since this etag was read. |
| actor | [ Actor](#actor) | none |
### RenameFoldResponse
(synq.entities.lineage.folds.v1alpha.RenameFoldResponse)
| Field | Type | Description |
| - | - | - |
| fold | [ LineageFold](#lineagefold) | none |
### SeparatedEntity
(synq.entities.lineage.folds.v1alpha.SeparatedEntity)
SeparatedEntity is a folded entity and the entities it is shown with today
that stay out of the fold.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | none |
| separated\_from\_entity\_ids | [repeated string](#string) | none |
### SetFoldEnabledRequest
(synq.entities.lineage.folds.v1alpha.SetFoldEnabledRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| enabled | [ bool](#bool) | none |
| actor | [ Actor](#actor) | none |
### SetFoldEnabledResponse
(synq.entities.lineage.folds.v1alpha.SetFoldEnabledResponse)
| Field | Type | Description |
| - | - | - |
| fold | [ LineageFold](#lineagefold) | none |
### UpdateFoldMatcherRequest
(synq.entities.lineage.folds.v1alpha.UpdateFoldMatcherRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| matcher | [ FoldMatcher](#foldmatcher) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | When set, the change is refused with ABORTED if the fold changed since this etag was read. |
| actor | [ Actor](#actor) | none |
### UpdateFoldMatcherResponse
(synq.entities.lineage.folds.v1alpha.UpdateFoldMatcherResponse)
| Field | Type | Description |
| - | - | - |
| fold | [ LineageFold](#lineagefold) | none |
### FoldMatcher
(synq.entities.lineage.folds.v1alpha.FoldMatcher)
FoldMatcher selects the entities a fold shows as its node.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) kind.id\_pattern | [ IdPatternMatcher](#idpatternmatcher) | Match entity ids against a pattern. |
### FoldNode
(synq.entities.lineage.folds.v1alpha.FoldNode)
FoldNode is one node lineage showed for a fold: the fold as it was applied,
and the entities the node stands for.
The node's entity id is derived from all of that, so it names exactly one set
of members. When the fold or its members change, lineage shows the fold under
a new id, and the old id keeps answering with what it showed while a reader
may still hold it.
| Field | Type | Description |
| - | - | - |
| entity\_id | [ string](#string) | The node's entity id, `lineagefold-` followed by a hash. |
| fold\_id | [ string](#string) | The fold the node was made from. It may since have been changed, switched off or deleted: read it with `BatchGetFolds`. |
| name | [ string](#string) | The fold's name when the node was made. |
| matcher | [ FoldMatcher](#foldmatcher) | The fold's matcher when the node was made. |
| member\_entity\_ids | [repeated string](#string) | The entities the node stands for, sorted. |
| created\_at | [ Timestamp](#timestamp) | When the node was made. |
### IdPatternMatcher
(synq.entities.lineage.folds.v1alpha.IdPatternMatcher)
IdPatternMatcher matches the entity ids of the entities to fold.
The pattern is an entity id with the part that changes replaced by
wildcards: `*` matches any run of characters and `?` exactly one. A wildcard
matches within one part of the id and never across the `::` between parts or
the `/` inside a storage path, so `bq-proj::ds::events_*` folds
`bq-proj::ds::events_20261008` but not anything inside another dataset, and
`s3-bucket::p-runs/*/orders` folds one path segment.
The first part of the id (the instance, project or bucket) is written out in
full, and the pattern holds at least one wildcard.
| Field | Type | Description |
| - | - | - |
| pattern | [ string](#string) | none |
### LineageFold
(synq.entities.lineage.folds.v1alpha.LineageFold)
LineageFold shows many similar entities as one node in lineage.
Some tools write to a new table or a new storage path on every run: a date
sharded table (`events_20261008`), a staging table per load
(`tmp_load_`), an export per day (`exports/orders_20261008`). Each of
them is its own entity, so lineage shows one node per day or per run. A fold
matches those entities and lineage shows them as the single fold node
instead, connected to everything any of them is connected to.
Folding changes only how lineage is drawn. Every folded entity keeps its own
page, checks, owners and documentation, and switching a fold off brings the
original nodes back.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the fold, a UUID chosen by the caller when the fold is created. |
| name | [ string](#string) | Name shown on the fold's node, for example "orders exports". |
| matcher | [ FoldMatcher](#foldmatcher) | Which entities the fold shows as its node. |
| enabled | [ bool](#bool) | Whether lineage applies the fold. A fold that is switched off is kept, so it can be switched back on. |
| etag | [ string](#string) | Entity tag for optimistic concurrency: pass it back on a change to guard against overwriting a concurrent one. Opaque, do not parse. Output only. |
| created\_at | [ Timestamp](#timestamp) | When the fold was created. Output only. |
| updated\_at | [ Timestamp](#timestamp) | When the fold was last changed, switching it on or off included. Output only. |
| created\_by | [ Actor](#actor) | Who created the fold. Absent when it cannot be attributed to a person. Output only. |
| updated\_by | [ Actor](#actor) | Who last changed the fold. Absent when it cannot be attributed to a person. Output only. |
### CllDetails
(synq.entities.lineage.v1.CllDetails)
| Field | Type | Description |
| - | - | - |
| columns | [repeated Column](#column) | Column details for CLL. |
| cll\_state | [ CllState](#cllstate) | State of the CLL parse. UNSPECIFIED if CLL was not requested. |
| cll\_messages | [repeated string](#string) | Messages related to CLL. e.g. Description of parse errors, etc. |
### Column
(synq.entities.lineage.v1.Column)
Column in a table-like asset (used in CLL mode).
| Field | Type | Description |
| - | - | - |
| column\_id | [ string](#string) | ID string for the column. This is the parsed column name. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_name.name | [optional string](#string) | Original column name as fetched from the table. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_native\_type.native\_type | [optional string](#string) | Column type as fetched from the table. |
### ColumnDependency
(synq.entities.lineage.v1.ColumnDependency)
Indicates data flow between columns.
Source columns are used to compute value of target columns.
| Field | Type | Description |
| - | - | - |
| source\_node\_idx | [ uint32](#uint32) | Index of source node in the lineage nodes list. |
| source\_node\_column\_id | [ string](#string) | none |
| target\_node\_idx | [ uint32](#uint32) | Index of target node in the lineage nodes list. |
| target\_node\_column\_id | [ string](#string) | none |
### Lineage
(synq.entities.lineage.v1.Lineage)
Lineage defines the lineage of table-like entities.
| Field | Type | Description |
| - | - | - |
| nodes | [repeated LineageNode](#lineagenode) | Nodes in the lineage with their identities and columns. |
| node\_dependencies | [repeated NodeDependency](#nodedependency) | All edges in the lineage between nodes. This can be parsed to create a graph of all the nodes. |
| is\_cll | [ bool](#bool) | Indicates whether the lineage was filtered for column level lineage (CLL). |
| column\_dependencies | [repeated ColumnDependency](#columndependency) | Dependencies between columns. Populated only for CLL. |
### LineageNode
(synq.entities.lineage.v1.LineageNode)
Node in a lineage graph representing one or more entities (e.g. database table).
| Field | Type | Description |
| - | - | - |
| ids | [repeated Identifier](#identifier) | All entities which have the same identity as this node. Must be at least one item. These are sorted by closeness to the type of the start point entities. e.g. if requesting lineage of a DBT source, first entity should be from DBT, similarly when viewing table it will be other tables. |
| position | [ NodePosition](#nodeposition) | Position of the node in the lineage. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cll\_details.cll\_details | [optional CllDetails](#clldetails) | Populated only for Column Level Lineage (CLL). |
### NodeDependency
(synq.entities.lineage.v1.NodeDependency)
Indicates data flow between nodes.
Source nodes are used to compute value of target nodes.
| Field | Type | Description |
| - | - | - |
| source\_node\_idx | [ uint32](#uint32) | Index of source node in the lineage nodes list. |
| target\_node\_idx | [ uint32](#uint32) | Index of target node in the lineage nodes list. |
### EntitiesStartPoint
(synq.entities.lineage.v1.EntitiesStartPoint)
| Field | Type | Description |
| - | - | - |
| entities | [repeated Identifier](#identifier) | none |
### EntityColumnsStartPoint
(synq.entities.lineage.v1.EntityColumnsStartPoint)
| Field | Type | Description |
| - | - | - |
| id | [ Identifier](#identifier) | none |
| column\_names | [repeated string](#string) | none |
### GetLineageRequest
(synq.entities.lineage.v1.GetLineageRequest)
| Field | Type | Description |
| - | - | - |
| lineage\_direction | [ LineageDirection](#lineagedirection) | none |
| start\_point | [ GetLineageStartPoint](#getlineagestartpoint) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_max\_depth.max\_depth | [optional int32](#int32) | none |
### GetLineageResponse
(synq.entities.lineage.v1.GetLineageResponse)
| Field | Type | Description |
| - | - | - |
| lineage | [ Lineage](#lineage) | none |
### GetLineageStartPoint
(synq.entities.lineage.v1.GetLineageStartPoint)
Possible starting points to get lineage from.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) from.entities | [ EntitiesStartPoint](#entitiesstartpoint) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) from.entity\_columns | [ EntityColumnsStartPoint](#entitycolumnsstartpoint) | none |
### EmailUser
(synq.users.v1.EmailUser)
| Field | Type | Description |
| - | - | - |
| user\_email | [ string](#string) | none |
### MsTeamsUser
(synq.users.v1.MsTeamsUser)
| Field | Type | Description |
| - | - | - |
| member\_id | [ string](#string) | none |
### PlatformUser
(synq.users.v1.PlatformUser)
PlatformUser links a login on one connected platform, such as a warehouse
user, to a workspace member, so what the login reads counts as that member's.
A login belongs to at most one member, and a member may hold many logins.
Linking a login that another member holds moves it to the new member.
| Field | Type | Description |
| - | - | - |
| platform\_user\_id | [ string](#string) | The platform user id, as the platform users reads return it. |
### SlackUser
(synq.users.v1.SlackUser)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_verified.verified | [optional bool](#bool) | True when the mapping was established by the user proving ownership of the Slack account through "Sign in with Slack" (OIDC), rather than being self-asserted. Server-set only — never trusted from client input. Empty (unset/false) means the mapping is self-asserted. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_team\_id.team\_id | [optional string](#string) | Slack workspace/team the user\_id belongs to. Set when the mapping was established via sign-in (a Slack user\_id is only unique within a team). Empty for self-asserted mappings. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_linked\_via.linked\_via | [optional string](#string) | How the mapping was established: "manual" (self-asserted selection) or "slack\_oauth" (verified sign-in). Empty is treated as "manual". |
### User
(synq.users.v1.User)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| first\_name | [ string](#string) | none |
| last\_name | [ string](#string) | none |
| email | [ string](#string) | none |
| identities | [repeated string](#string) | Canonical identity strings for this user. Includes "synq:\" and all linked aliases (email, slack, msteams, and every linked platform user as its "platform\_user:" id). Can be passed to ListIssues(interacted\_by\_identities) to filter issues this user interacted with. |
| roles | [repeated UserRole](#userrole) | Roles the user holds in the workspace. Empty when the user holds no role that can be managed through this API. |
| status | [ UserStatus](#userstatus) | Whether the user has accepted their invitation to the workspace. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_last\_login\_at.last\_login\_at | [optional Timestamp](#timestamp) | Most recent interactive sign-in to the workspace, or to any workspace when no sign-in to this one has been recorded. Unset when the user has not signed in since sign-ins began being recorded. Token refreshes and API calls do not count as sign-ins. |
| created\_at | [ Timestamp](#timestamp) | When the user account was created. |
### UserAlias
(synq.users.v1.UserAlias)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.slack | [ SlackUser](#slackuser) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.email | [ EmailUser](#emailuser) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.ms\_teams | [ MsTeamsUser](#msteamsuser) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) type.platform\_user | [ PlatformUser](#platformuser) | none |
### AddUserRoleRequest
(synq.users.v1.AddUserRoleRequest)
| Field | Type | Description |
| - | - | - |
| identity | [ string](#string) | One of the user's identities, as listed in User.identities or returned by GetUserIdentities: "synq:\", "email:\", "slack:\" or "msteams:\". |
| role | [ UserRole](#userrole) | none |
### AddUserRoleResponse
(synq.users.v1.AddUserRoleResponse)
| Field | Type | Description |
| - | - | - |
| user | [ User](#user) | The user as stored after the call. |
### BatchGetUsersRequest
(synq.users.v1.BatchGetUsersRequest)
| Field | Type | Description |
| - | - | - |
| user\_ids | [repeated string](#string) | none |
### BatchGetUsersResponse
(synq.users.v1.BatchGetUsersResponse)
| Field | Type | Description |
| - | - | - |
| users | [repeated User](#user) | none |
### DeleteUserAliasesRequest
(synq.users.v1.DeleteUserAliasesRequest)
| Field | Type | Description |
| - | - | - |
| aliases | [repeated UserAlias](#useralias) | none |
### DeleteUserAliasesResponse
(synq.users.v1.DeleteUserAliasesResponse)
### GetByAliasRequest
(synq.users.v1.GetByAliasRequest)
| Field | Type | Description |
| - | - | - |
| alias | [ UserAlias](#useralias) | none |
### GetByAliasResponse
(synq.users.v1.GetByAliasResponse)
| Field | Type | Description |
| - | - | - |
| user | [ User](#user) | none |
### GetByEmailRequest
(synq.users.v1.GetByEmailRequest)
| Field | Type | Description |
| - | - | - |
| email | [ string](#string) | none |
### GetByEmailResponse
(synq.users.v1.GetByEmailResponse)
| Field | Type | Description |
| - | - | - |
| user | [ User](#user) | none |
### GetUserAliasesRequest
(synq.users.v1.GetUserAliasesRequest)
| Field | Type | Description |
| - | - | - |
| user\_id | [ string](#string) | none |
### GetUserAliasesResponse
(synq.users.v1.GetUserAliasesResponse)
| Field | Type | Description |
| - | - | - |
| aliases | [repeated UserAlias](#useralias) | none |
### GetUserIdentitiesRequest
(synq.users.v1.GetUserIdentitiesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user\_id.user\_id | [optional string](#string) | User ID to resolve identities for. If omitted, uses the currently authenticated user. |
### GetUserIdentitiesResponse
(synq.users.v1.GetUserIdentitiesResponse)
| Field | Type | Description |
| - | - | - |
| identities | [repeated string](#string) | Canonical identity strings for the user. Always includes "synq:\" and all linked aliases. |
### ListUsersRequest
(synq.users.v1.ListUsersRequest)
### ListUsersResponse
(synq.users.v1.ListUsersResponse)
| Field | Type | Description |
| - | - | - |
| users | [repeated User](#user) | none |
### RemoveUserRoleRequest
(synq.users.v1.RemoveUserRoleRequest)
| Field | Type | Description |
| - | - | - |
| identity | [ string](#string) | One of the user's identities, as listed in User.identities or returned by GetUserIdentities: "synq:\", "email:\", "slack:\" or "msteams:\". |
| role | [ UserRole](#userrole) | none |
### RemoveUserRoleResponse
(synq.users.v1.RemoveUserRoleResponse)
| Field | Type | Description |
| - | - | - |
| user | [ User](#user) | The user as stored after the call. Unset when the call removed the user from the workspace, or the user was not a member. |
### UpsertUserAliasesRequest
(synq.users.v1.UpsertUserAliasesRequest)
| Field | Type | Description |
| - | - | - |
| aliases | [repeated UserAlias](#useralias) | none |
### UpsertUserAliasesResponse
(synq.users.v1.UpsertUserAliasesResponse)
### BatchGetRequest
(synq.savedviews.v1.BatchGetRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Ids to fetch (1–200, unique). |
### BatchGetResponse
(synq.savedviews.v1.BatchGetResponse)
| Field | Type | Description |
| - | - | - |
| views | [map ViewsEntry](#viewsentry) | Views keyed by id. Ids the caller cannot see or that do not exist are absent. |
### BatchGetResponse.ViewsEntry
(synq.savedviews.v1.BatchGetResponse.ViewsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ SavedView](#savedview) | none |
### DeleteRequest
(synq.savedviews.v1.DeleteRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the view to delete. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
### DeleteResponse
(synq.savedviews.v1.DeleteResponse)
### ListRequest
(synq.savedviews.v1.ListRequest)
| Field | Type | Description |
| - | - | - |
| scopes | [repeated SavedViewRelationship](#savedviewrelationship) | Relationship buckets to include. Empty returns every view the caller can see. For example \[SAVED\_VIEW\_RELATIONSHIP\_MINE] returns only the caller's own views. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_context.context | [optional SavedViewContext](#savedviewcontext) | Restrict to a single surface. Omit to return views for all contexts. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_only\_pinned.only\_pinned | [optional bool](#bool) | Restrict to views the caller has pinned. |
| pagination | [ Pagination](#pagination) | Pagination. |
### ListResponse
(synq.savedviews.v1.ListResponse)
| Field | Type | Description |
| - | - | - |
| views | [repeated SavedView](#savedview) | The matching views. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### SetPinRequest
(synq.savedviews.v1.SetPinRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the view to pin/unpin for the calling user. |
| pinned | [ bool](#bool) | Desired pin state: true to pin, false to unpin. |
### SetPinResponse
(synq.savedviews.v1.SetPinResponse)
### UpsertRequest
(synq.savedviews.v1.UpsertRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Client-supplied UUID identifying the view. The same id updates the same view; a new id creates a new view. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | Title. Required when creating; omit to keep the existing title on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Description. Omit to keep the existing description. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_context.context | [optional SavedViewContext](#savedviewcontext) | Surface the view applies to. Omit to keep the existing context. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_selection.selection | [optional SavedViewSelection](#savedviewselection) | The selection. Required when creating; omit to keep the existing selection. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config.config | [optional SavedViewConfig](#savedviewconfig) | Display configuration. Omit to keep the existing config. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_visibility.visibility | [optional SavedViewVisibility](#savedviewvisibility) | Sharing state. Omit to keep the existing visibility. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_grants.grants | [optional GrantList](#grantlist) | Access grants. When present, replaces the entire grant set (an empty list clears all grants); when omitted, grants are left unchanged. Changing grants requires manage/admin permission on the view. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the update fails with a conflict if the view was modified since this etag was read. |
### UpsertResponse
(synq.savedviews.v1.UpsertResponse)
| Field | Type | Description |
| - | - | - |
| view | [ SavedView](#savedview) | The stored view after the upsert. |
### Grant
(synq.savedviews.v1.Grant)
A single access grant on a saved view.
| Field | Type | Description |
| - | - | - |
| grantee\_identity | [ string](#string) | Identity of the grantee. Currently a user email (`user@example.com`); the view becomes visible to that user once they exist in the workspace, so a view can be shared with someone before their first login. |
| role | [ GrantRole](#grantrole) | What the grantee may do. Defaults to VIEW. |
### GrantList
(synq.savedviews.v1.GrantList)
A wrapper giving the grant set explicit presence on write, so a partial
update can distinguish "replace the grants" (present, possibly empty) from
"leave grants unchanged" (absent).
| Field | Type | Description |
| - | - | - |
| grants | [repeated Grant](#grant) | none |
### SavedView
(synq.savedviews.v1.SavedView)
A saved view: a named, reusable selection over entities (or another context)
with display configuration, sharing and per-user pinning.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Client-supplied UUID identifying the view. |
| context | [ SavedViewContext](#savedviewcontext) | The surface this view applies to. |
| title | [ string](#string) | Human-readable title. |
| description | [ string](#string) | Optional longer description. |
| selection | [ SavedViewSelection](#savedviewselection) | The selection captured by the view. |
| config | [ SavedViewConfig](#savedviewconfig) | Display configuration. |
| visibility | [ SavedViewVisibility](#savedviewvisibility) | Sharing state. |
| grants | [repeated Grant](#grant) | Access grants (populated for GRANTED views). |
| owner | [ Actor](#actor) | Who created the view. Output-only. |
| updated\_by | [ Actor](#actor) | Who last edited the view. Output-only. |
| relationship | [ SavedViewRelationship](#savedviewrelationship) | How this view relates to the calling user. Output-only. |
| permissions | [ SavedViewPermissions](#savedviewpermissions) | What the calling user may do with this view. Output-only. |
| pinned | [ bool](#bool) | Whether the calling user has pinned this view to their main screen. Output-only; change with SetPin. |
| etag | [ string](#string) | Entity tag for optimistic concurrency: pass back on Upsert/Delete to guard against overwriting a concurrent change. Form: `W/"-"`. |
| created\_at | [ Timestamp](#timestamp) | When the view was created. Output-only. |
| updated\_at | [ Timestamp](#timestamp) | When the view was last updated. Output-only. |
### SavedViewConfig
(synq.savedviews.v1.SavedViewConfig)
How a saved view is displayed once opened. This message is intentionally
permissive and forward-compatible: new presentation options are added as new
fields, never as a breaking change, so older clients keep working.
| Field | Type | Description |
| - | - | - |
| layout | [ Layout](#layout) | How results are laid out (list, cards, table). Unspecified lets the client pick its default for the context. |
| card\_fields | [repeated string](#string) | Which fields to surface on each result (e.g. "last\_run", "owner", "status", "platform"). Interpreted by the client; unknown field names are ignored. |
### SavedViewPermissions
(synq.savedviews.v1.SavedViewPermissions)
What the calling user is allowed to do with a saved view. Output-only:
computed per request from ownership, the caller's grant (if any) and their
workspace permissions. Viewing and pinning are always allowed for any view
the caller can see, so they are not represented here.
| Field | Type | Description |
| - | - | - |
| can\_edit | [ bool](#bool) | The caller can update the view's definition, title, description and config. |
| can\_delete | [ bool](#bool) | The caller can delete the view. |
| can\_manage\_grants | [ bool](#bool) | The caller can change the view's sharing/visibility and grants. |
### SavedViewQuery
(synq.savedviews.v1.SavedViewQuery)
The query behind a saved view, expressed for the public API.
On write, provide the query in ONE of two forms:
* `public_query` — a structured query (the same shape used elsewhere for
asset selection), convenient to build from a filter UI; or
* `resolver_ql` — the SYNQ resolver query language, a compact text form
that additionally expresses severity/status and time-window filters
(e.g. `with_severity(statuses=["error"], since="24h")`).
When both are set, `resolver_ql` wins. The server compiles the query and
stores it canonically; the raw text is not preserved verbatim.
On read, the server returns `rendered_resolver_ql` (the stored query rendered
back to canonical resolver query language) and, best-effort, `public_query`
(the structured representation; parts with no structured equivalent are marked
unsupported). `resolver_ql` is not echoed back — read `rendered_resolver_ql`.
| Field | Type | Description |
| - | - | - |
| resolver\_ql | [ string](#string) | WRITE (option A). The query in resolver query language. Superset form — the only way to express severity/status and time-window filters. |
| rendered\_resolver\_ql | [ string](#string) | READ, output-only. The stored query rendered back to canonical resolver query language. Deterministically derived; does not preserve original hand-typed formatting or comments. |
| public\_query | [ Query](#query) | WRITE (option B) and READ. On write, the structured query to store (used when `resolver_ql` is empty). On read, a structured representation of the stored query — lossy: parts with no structured equivalent are represented as unsupported, so prefer `rendered_resolver_ql` for full fidelity. |
### SavedViewSelection
(synq.savedviews.v1.SavedViewSelection)
The selection that a saved view captures. Modelled as a oneof so future
contexts (e.g. an issues filter) can add their own selection shape without
reshaping the view.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.entity\_query | [ SavedViewQuery](#savedviewquery) | Entity-backed selection (catalog/assets and checks contexts). |
### CatalogAccount
(synq.auth.iam.v1.CatalogAccount)
A Coalesce Catalog account linked to a Coalesce Quality workspace.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Catalog's id of the account. |
| name | [ string](#string) | Name of the account in Coalesce Catalog. |
### IamRequest
(synq.auth.iam.v1.IamRequest)
### IamResponse
(synq.auth.iam.v1.IamResponse)
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Workspace to which caller is authenticated |
| scopes | [repeated Scope](#scope) | What scopes are granted |
| scope\_names | [repeated string](#string) | Names of the scopes available |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_integration\_id.integration\_id | [optional string](#string) | Indicates that the token is scoped to a specific integration |
| subject | [ string](#string) | Identifier of the provided token |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_synq\_user\_id.synq\_user\_id | [optional string](#string) | Coalesce Quality user ID associated with the token, present for user-level credentials |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_client\_type.client\_type | [optional string](#string) | Indicates the client type through which the request was made |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user\_name.user\_name | [optional string](#string) | Human-readable name of the authenticated user. Populated only when the token is tied to a Coalesce Quality user (i.e. `synq_user_id` is set); empty for workspace-level or third-party tokens. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user\_email.user\_email | [optional string](#string) | Email address of the authenticated user. Populated only when the token is tied to a Coalesce Quality user (i.e. `synq_user_id` is set); empty for workspace-level or third-party tokens. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_catalog\_account.catalog\_account | [optional CatalogAccount](#catalogaccount) | Coalesce Catalog account the workspace is linked to. Unset when the workspace has no Catalog account, so nothing Catalog-backed (knowledge pages, Catalog-written descriptions) has anything to answer. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_time\_zone.time\_zone | [optional string](#string) | IANA time zone the workspace works in, such as "Europe/Madrid". Unset when the workspace has not set one. A client showing activity by local time uses `user_time_zone` when set, then this, then UTC. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user\_time\_zone.user\_time\_zone | [optional string](#string) | IANA time zone the authenticated user chose, such as "Europe/Warsaw". Populated only when the token is tied to a Coalesce Quality user (i.e. `synq_user_id` is set) and that user has chosen a zone; unset otherwise. Takes precedence over `time_zone`. |
### BatchGetEntityKnowledgeRequest
(synq.knowledge.v1alpha.BatchGetEntityKnowledgeRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Entities to read (1–100, unique), by the opaque entity id other APIs return. A caller holding structured coordinates converts them once with `synq.entities.resolve.v1.IdentifierResolveService.BatchResolveIdentifiers`. |
### BatchGetEntityKnowledgeResponse
(synq.knowledge.v1alpha.BatchGetEntityKnowledgeResponse)
| Field | Type | Description |
| - | - | - |
| knowledge | [map KnowledgeEntry](#knowledgeentry) | Knowledge keyed by entity id. Entities with no knowledge are absent. |
### BatchGetEntityKnowledgeResponse.KnowledgeEntry
(synq.knowledge.v1alpha.BatchGetEntityKnowledgeResponse.KnowledgeEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ EntityKnowledge](#entityknowledge) | none |
### BatchGetKnowledgePagesRequest
(synq.knowledge.v1alpha.BatchGetKnowledgePagesRequest)
| Field | Type | Description |
| - | - | - |
| page\_ids | [repeated string](#string) | Knowledge pages to read (1–100, unique), by id. |
| skip\_content | [ bool](#bool) | Leave each page's `markdown` out. Everything else is read as usual. |
### BatchGetKnowledgePagesResponse
(synq.knowledge.v1alpha.BatchGetKnowledgePagesResponse)
| Field | Type | Description |
| - | - | - |
| pages | [map PagesEntry](#pagesentry) | Pages keyed by id. Pages that do not exist or were deleted are absent. |
### BatchGetKnowledgePagesResponse.PagesEntry
(synq.knowledge.v1alpha.BatchGetKnowledgePagesResponse.PagesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ KnowledgePage](#knowledgepage) | none |
### EntityKnowledge
(synq.knowledge.v1alpha.EntityKnowledge)
The knowledge held about an entity.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional KnowledgeDescription](#knowledgedescription) | The description people wrote for the entity. Unset when nobody wrote one. The description the asset's own source carries, such as a warehouse comment or a dbt description, is not repeated here: the entity itself reports it. |
| pages | [repeated KnowledgePageRef](#knowledgepageref) | The knowledge pages pinned to the entity or pinning it, ordered by name. Read their content with `BatchGetKnowledgePages`. |
### KnowledgeDescription
(synq.knowledge.v1alpha.KnowledgeDescription)
A description people wrote for an entity.
| Field | Type | Description |
| - | - | - |
| markdown | [ string](#string) | The description, as Markdown. |
| generated | [ bool](#bool) | Whether it was generated rather than written by a person. |
### KnowledgeOwner
(synq.knowledge.v1alpha.KnowledgeOwner)
Someone who owns a knowledge page.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | The person's full name, or the team's name. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_email.email | [optional string](#string) | The person's or team's email, when known. |
| team | [ bool](#bool) | Whether the owner is a team rather than a person. |
### KnowledgePage
(synq.knowledge.v1alpha.KnowledgePage)
A knowledge page: a document the team wrote, or imported from a tool such
as Confluence or Notion.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | The page's id. |
| name | [ string](#string) | none |
| markdown | [ string](#string) | The page's content, as Markdown. Empty when the page has none, or when it was read with `skip_content`. |
| generated | [ bool](#bool) | Whether the content was generated rather than written by a person. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_url.url | [optional string](#string) | Where the page was imported from. Unset for a page written here. |
| updated\_at | [ Timestamp](#timestamp) | When the page last changed. |
| ancestors | [repeated KnowledgePageRef](#knowledgepageref) | The pages above this one, from the top of the tree down to its parent. Empty for a page at the top. |
| children | [repeated KnowledgePageRef](#knowledgepageref) | The pages directly under this one, ordered by name. |
| certified | [ bool](#bool) | Whether someone certified the page as trustworthy. |
| deprecated | [ bool](#bool) | none |
| owners | [repeated KnowledgeOwner](#knowledgeowner) | The people and teams who own the page. |
| tags | [repeated string](#string) | The page's tags, sorted. |
### KnowledgePageRef
(synq.knowledge.v1alpha.KnowledgePageRef)
Names a knowledge page.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | The page's id. |
| name | [ string](#string) | none |
### ListKnowledgePageLinksRequest
(synq.knowledge.v1alpha.ListKnowledgePageLinksRequest)
| Field | Type | Description |
| - | - | - |
| page\_id | [ string](#string) | The knowledge page, by id. |
### ListKnowledgePageLinksResponse
(synq.knowledge.v1alpha.ListKnowledgePageLinksResponse)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Entities pinned to the page or pinning it, sorted. |
| page\_ids | [repeated string](#string) | Other knowledge pages pinned to the page or pinning it, ordered by name. |
### ListKnowledgePagesRequest
(synq.knowledge.v1alpha.ListKnowledgePagesRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_name\_contains.name\_contains | [optional string](#string) | Keep pages whose name contains this, ignoring case. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_certified.certified | [optional bool](#bool) | Keep only certified pages (true) or only uncertified ones (false). |
| include\_deprecated | [ bool](#bool) | Include deprecated pages, which are left out by default. |
| page\_size | [ int32](#int32) | Pages per response, 1–100. Defaults to 50. |
| page\_token | [ string](#string) | `next_page_token` of the previous response, to read the next one. |
### ListKnowledgePagesResponse
(synq.knowledge.v1alpha.ListKnowledgePagesResponse)
| Field | Type | Description |
| - | - | - |
| page\_ids | [repeated string](#string) | Knowledge page ids, ordered by the pages' names. |
| next\_page\_token | [ string](#string) | Pass as `page_token` to read on. Empty on the last response. |
| total\_count | [ int32](#int32) | How many pages match, across every response. |
### Dataproduct
(synq.dataproducts.v2.Dataproduct)
A data product: a named, owned grouping of data assets that share a purpose,
with a membership definition, a priority and an optional folder.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the data product (a UUID). Supplied by the caller on create, which makes Upsert idempotent. |
| entity\_id | [ string](#string) | Stable, opaque platform identifier of this data product (of the form "dataproduct-\"). Output-only — the server derives it; never construct it by hand. This is the value other SYNQ APIs accept as an entity reference for this product (lineage, entities, alerts), so read it here and pass it there to point at the same product without needing to know how it is formed. `synq.owners.v1.OwnershipSelection.dataproduct_id` accepts this `entity_id` directly (or the bare `id`). |
| title | [ string](#string) | Human-readable title. |
| description | [ string](#string) | Optional longer description. |
| folder | [ string](#string) | Optional free-text folder used to group products in the UI. |
| priority | [ Priority](#priority) | none |
| source | [ Source](#source) | none |
| definition | [ DataproductDefinition](#dataproductdefinition) | The membership definition. Omitted when a read requests `exclude_definition`. |
| etag | [ string](#string) | Entity tag for optimistic concurrency: pass back on Upsert / Delete / definition writes to guard against overwriting a concurrent change. Form: `W/"-"`. Opaque — do not parse. |
| created\_by | [ Actor](#actor) | Who created the data product. Output-only. |
| updated\_by | [ Actor](#actor) | Who last edited the data product. Output-only. |
| created\_at | [ Timestamp](#timestamp) | When the data product was created. Output-only. |
| updated\_at | [ Timestamp](#timestamp) | When the data product was last updated. Output-only. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | When the data product was soft-deleted, if it has been. Output-only. |
### DataproductDefinition
(synq.dataproducts.v2.DataproductDefinition)
The membership of a data product: which assets belong to it. A definition is
an ordered list of parts; an asset is a member if it matches ANY part (the
parts are OR'd together).
A data product is a LEAF in the reference graph: its membership is defined
only by asset-level predicates, static asset ids and lineage of those — never
by referencing another data product or domain. The `in_dataproduct` /
`in_domain` query operands are therefore rejected anywhere in a data product
definition (see DataproductQuery). This keeps membership resolution finite:
data products can never take part in a reference cycle.
| Field | Type | Description |
| - | - | - |
| parts | [repeated Part](#part) | The parts of the definition. An asset is a member if it matches any part. |
### DataproductDefinition.Part
(synq.dataproducts.v2.DataproductDefinition.Part)
A single piece of a data product definition: either a pinned asset or a
selection query.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Stable id of this part (a UUID). Supplied by the caller for idempotent part upserts; the server mints one if left empty on create. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.entity\_id | [ string](#string) | Pin a single asset by its opaque entity id (as returned by resolve / listing APIs). Never constructed by hand. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ DataproductQuery](#dataproductquery) | Select assets by a query. |
### DataproductQuery
(synq.dataproducts.v2.DataproductQuery)
The selection query behind a data product part, expressed for the public API.
On write, provide the query in ONE of two forms:
* `public_query` — a structured query (the same shape used elsewhere for
asset selection), convenient to build from a filter UI; or
* `resolver_ql` — the resolver query language, a compact text form that is
the canonical way to author selections as code.
When both are set, `resolver_ql` wins. The server compiles the query and
stores it canonically; the raw text is not preserved verbatim.
On read, the server returns `rendered_resolver_ql` (the stored query rendered
back to canonical resolver query language) and, best-effort, `public_query`
(the structured representation). `resolver_ql` is not echoed back — read
`rendered_resolver_ql`.
Leaf rule: a data product may not reference another data product or domain.
The `in_dataproduct`, `in_domain` and `unsupported` operands are rejected at
the top level here (CEL) and, for nested sub-queries, by a server-side walk.
| Field | Type | Description |
| - | - | - |
| resolver\_ql | [ string](#string) | WRITE (option A). The query in resolver query language. |
| rendered\_resolver\_ql | [ string](#string) | READ, output-only. The stored query rendered back to canonical resolver query language. Deterministically derived; does not preserve original hand-typed formatting or comments. |
| public\_query | [ Query](#query) | WRITE (option B) and READ. On write, the structured query to store (used when `resolver_ql` is empty). On read, a best-effort structured representation of the stored query. |
### BatchGetRequest
(synq.dataproducts.v2.BatchGetRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Ids to fetch (1–200, unique). Each accepts either the bare UUID or the `entity_id` form (`dataproduct-`); the prefix is stripped server-side. |
| include\_deleted | [ bool](#bool) | Include soft-deleted products. |
| exclude\_definition | [ bool](#bool) | Omit the (potentially large) membership definition from each product. |
### BatchGetResponse
(synq.dataproducts.v2.BatchGetResponse)
| Field | Type | Description |
| - | - | - |
| dataproducts | [map DataproductsEntry](#dataproductsentry) | Products keyed by id. Ids that do not exist (or are deleted, unless requested) are absent. |
### BatchGetResponse.DataproductsEntry
(synq.dataproducts.v2.BatchGetResponse.DataproductsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Dataproduct](#dataproduct) | none |
### DeleteRequest
(synq.dataproducts.v2.DeleteRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the product to delete. Accepts the bare UUID or the `dataproduct-` entity\_id form (the prefix is stripped server-side). |
| purge | [ bool](#bool) | Hard-delete instead of soft-delete, releasing the id for reuse. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
### DeleteResponse
(synq.dataproducts.v2.DeleteResponse)
### ListMembersRequest
(synq.dataproducts.v2.ListMembersRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the product whose members to list. Accepts the bare UUID or the `dataproduct-` entity\_id form (the prefix is stripped server-side). |
| pagination | [ Pagination](#pagination) | Pagination. |
### ListMembersResponse
(synq.dataproducts.v2.ListMembersResponse)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated string](#string) | Opaque entity ids of the assets that currently belong to the product. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### ListRequest
(synq.dataproducts.v2.ListRequest)
| Field | Type | Description |
| - | - | - |
| pagination | [ Pagination](#pagination) | Pagination. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_folder.folder | [optional string](#string) | Restrict to a single folder. |
| priorities | [repeated Priority](#priority) | Restrict to these priorities. Empty returns all priorities. |
| include\_deleted | [ bool](#bool) | Include soft-deleted products. |
| exclude\_definition | [ bool](#bool) | Omit the membership definition from each product. |
### ListResponse
(synq.dataproducts.v2.ListResponse)
| Field | Type | Description |
| - | - | - |
| dataproducts | [repeated Dataproduct](#dataproduct) | The matching products. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### RemoveDefinitionPartRequest
(synq.dataproducts.v2.RemoveDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the product. Accepts the bare UUID or the `dataproduct-` entity\_id form (the prefix is stripped server-side). |
| part\_id | [ string](#string) | Id of the part to remove. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### RemoveDefinitionPartResponse
(synq.dataproducts.v2.RemoveDefinitionPartResponse)
| Field | Type | Description |
| - | - | - |
| dataproduct | [ Dataproduct](#dataproduct) | The stored product after the part removal. |
### SetDefinitionRequest
(synq.dataproducts.v2.SetDefinitionRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the product whose definition to replace. Accepts the bare UUID or the `dataproduct-` entity\_id form (the prefix is stripped server-side). |
| definition | [ DataproductDefinition](#dataproductdefinition) | The new membership definition (replaces the existing one entirely). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### SetDefinitionResponse
(synq.dataproducts.v2.SetDefinitionResponse)
| Field | Type | Description |
| - | - | - |
| dataproduct | [ Dataproduct](#dataproduct) | The stored product after the definition write. |
### UpsertDefinitionPartRequest
(synq.dataproducts.v2.UpsertDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the product. Accepts the bare UUID or the `dataproduct-` entity\_id form (the prefix is stripped server-side). |
| part | [ Part](#part) | The part to add or replace (matched by its part id). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### UpsertDefinitionPartResponse
(synq.dataproducts.v2.UpsertDefinitionPartResponse)
| Field | Type | Description |
| - | - | - |
| dataproduct | [ Dataproduct](#dataproduct) | The stored product after the part write. |
### UpsertRequest
(synq.dataproducts.v2.UpsertRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the product. Accepts either the bare UUID or the `entity_id` form this API returns (`dataproduct-`) — the prefix is stripped server-side, so the value you read back from any API can be passed straight in. The same id updates the same product; a new id creates a new product. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | Title. Required when creating; omit to keep the existing title on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | Description. Omit to keep the existing description. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_folder.folder | [optional string](#string) | Folder. Omit to keep the existing folder; pass an empty string to clear it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_priority.priority | [optional Priority](#priority) | Priority. Omit to keep the existing priority. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source.source | [optional Source](#source) | Source. Omit to keep the existing source (defaults to SOURCE\_SYNQ on create). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_definition.definition | [optional DataproductDefinition](#dataproductdefinition) | Membership definition. Omit to keep the existing definition; pass a present (possibly empty) definition to replace it. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the update fails with a conflict if the product was modified since this etag was read. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### UpsertResponse
(synq.dataproducts.v2.UpsertResponse)
| Field | Type | Description |
| - | - | - |
| dataproduct | [ Dataproduct](#dataproduct) | The stored product after the upsert. |
### Dataproduct
(synq.dataproducts.v1.Dataproduct)
| Field | Type | Description |
| - | - | - |
| identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
| definition | [ DataproductDefinition](#dataproductdefinition) | none |
| title | [ string](#string) | none |
| description | [ string](#string) | none |
| folder | [ string](#string) | none |
| priority | [ Priority](#priority) | none |
| created\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | none |
| source | [ Source](#source) | Source for creation and management of dataproduct. |
### AssetSelectionQuery
(synq.dataproducts.v1.AssetSelectionQuery)
| Field | Type | Description |
| - | - | - |
| parts | [repeated QueryPart](#querypart) | none |
| operand | [ QueryOperand](#queryoperand) | none |
### AssetSelectionQuery.QueryPart
(synq.dataproducts.v1.AssetSelectionQuery.QueryPart)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier\_list | [ IdentifierList](#identifierlist) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_name\_search | [ WithNameSearch](#withnamesearch) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_type | [ WithType](#withtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_annotation | [ WithAnnotation](#withannotation) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_data\_platform | [ InDataPlatform](#indataplatform) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.in\_folder | [ InFolder](#infolder) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_data\_platform\_type | [ WithDataPlatformType](#withdataplatformtype) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identities\_of | [ IdentitiesOf](#identitiesof) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.with\_default\_visible\_types | [ WithDefaultVisibleTypes](#withdefaultvisibletypes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ AssetSelectionQuery](#assetselectionquery) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.unsupported | [ Unsupported](#unsupported) | none |
### DataproductDefinition
(synq.dataproducts.v1.DataproductDefinition)
| Field | Type | Description |
| - | - | - |
| parts | [repeated Part](#part) | none |
### DataproductDefinition.Part
(synq.dataproducts.v1.DataproductDefinition.Part)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | part identifier |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.identifier | [ Identifier](#identifier) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) part.query | [ AssetSelectionQuery](#assetselectionquery) | none |
### DeleteRequest
(synq.dataproducts.v1.DeleteRequest)
| Field | Type | Description |
| - | - | - |
| identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
### DeleteResponse
(synq.dataproducts.v1.DeleteResponse)
### GetRequest
(synq.dataproducts.v1.GetRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated DataproductIdentifier](#dataproductidentifier) | none |
### GetResponse
(synq.dataproducts.v1.GetResponse)
| Field | Type | Description |
| - | - | - |
| products | [repeated Dataproduct](#dataproduct) | none |
### ListRequest
(synq.dataproducts.v1.ListRequest)
| Field | Type | Description |
| - | - | - |
| include\_deleted | [ bool](#bool) | none |
| exclude\_definition | [ bool](#bool) | none |
### ListResponse
(synq.dataproducts.v1.ListResponse)
| Field | Type | Description |
| - | - | - |
| products | [repeated Dataproduct](#dataproduct) | none |
### RemoveDefinitionPartRequest
(synq.dataproducts.v1.RemoveDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| product\_identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
| part\_id | [ string](#string) | none |
### RemoveDefinitionPartResponse
(synq.dataproducts.v1.RemoveDefinitionPartResponse)
### SetDefinitionRequest
(synq.dataproducts.v1.SetDefinitionRequest)
| Field | Type | Description |
| - | - | - |
| product\_identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
| definition | [ DataproductDefinition](#dataproductdefinition) | none |
### SetDefinitionResponse
(synq.dataproducts.v1.SetDefinitionResponse)
### UpsertDefinitionPartRequest
(synq.dataproducts.v1.UpsertDefinitionPartRequest)
| Field | Type | Description |
| - | - | - |
| product\_identifier | [ DataproductIdentifier](#dataproductidentifier) | none |
| part | [ Part](#part) | none |
### UpsertDefinitionPartResponse
(synq.dataproducts.v1.UpsertDefinitionPartResponse)
### UpsertRequest
(synq.dataproducts.v1.UpsertRequest)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_identifier.identifier | [optional DataproductIdentifier](#dataproductidentifier) | none |
| title | [ string](#string) | none |
| description | [ string](#string) | none |
| folder | [ string](#string) | none |
| priority | [ Priority](#priority) | none |
| source | [ Source](#source) | none |
### UpsertResponse
(synq.dataproducts.v1.UpsertResponse)
| Field | Type | Description |
| - | - | - |
| identifier | [ DataproductIdentifier](#dataproductidentifier) | identifier of the updated or created dataproduct |
### BatchGetOwnersRequest
(synq.owners.v1.BatchGetOwnersRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Owner ids to fetch (1–200, unique). Each accepts either the bare UUID or the `entity_id` form (`owner-`); the prefix is stripped server-side. |
| include\_deleted | [ bool](#bool) | Include soft-deleted owners. |
### BatchGetOwnersResponse
(synq.owners.v1.BatchGetOwnersResponse)
| Field | Type | Description |
| - | - | - |
| owners | [map OwnersEntry](#ownersentry) | Owners keyed by id. Ids that do not exist (or are deleted, unless requested) are absent. |
### BatchGetOwnersResponse.OwnersEntry
(synq.owners.v1.BatchGetOwnersResponse.OwnersEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Owner](#owner) | none |
### BatchGetOwnershipsRequest
(synq.owners.v1.BatchGetOwnershipsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | Ownership ids to fetch (1–200, unique). |
| include\_deleted | [ bool](#bool) | Include soft-deleted ownerships. |
### BatchGetOwnershipsResponse
(synq.owners.v1.BatchGetOwnershipsResponse)
| Field | Type | Description |
| - | - | - |
| ownerships | [map OwnershipsEntry](#ownershipsentry) | Ownerships keyed by id. Ids that do not exist (or are deleted, unless requested) are absent. |
### BatchGetOwnershipsResponse.OwnershipsEntry
(synq.owners.v1.BatchGetOwnershipsResponse.OwnershipsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ Ownership](#ownership) | none |
### ContactList
(synq.owners.v1.ContactList)
A wrapper giving the contact set explicit presence on write, so a partial
update can distinguish "replace the contacts" (present, possibly empty) from
"leave contacts unchanged" (absent).
| Field | Type | Description |
| - | - | - |
| contacts | [repeated Contact](#contact) | none |
### DeleteOwnerRequest
(synq.owners.v1.DeleteOwnerRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the owner to delete. Accepts the bare UUID or the `owner-` entity\_id form (the prefix is stripped server-side). |
| purge | [ bool](#bool) | Hard-delete instead of soft-delete, releasing the id for reuse. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional. |
### DeleteOwnerResponse
(synq.owners.v1.DeleteOwnerResponse)
### DeleteOwnershipRequest
(synq.owners.v1.DeleteOwnershipRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Id of the ownership to delete. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional. |
### DeleteOwnershipResponse
(synq.owners.v1.DeleteOwnershipResponse)
### ListOwnersRequest
(synq.owners.v1.ListOwnersRequest)
| Field | Type | Description |
| - | - | - |
| pagination | [ Pagination](#pagination) | Pagination. |
| include\_deleted | [ bool](#bool) | Include soft-deleted owners. |
### ListOwnersResponse
(synq.owners.v1.ListOwnersResponse)
| Field | Type | Description |
| - | - | - |
| owners | [repeated Owner](#owner) | The matching owners. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### ListOwnershipsRequest
(synq.owners.v1.ListOwnershipsRequest)
| Field | Type | Description |
| - | - | - |
| owner\_id | [ string](#string) | Id of the owner whose ownerships to list. Accepts the bare UUID or the `owner-` entity\_id form (the prefix is stripped server-side). |
| pagination | [ Pagination](#pagination) | Pagination. |
| include\_deleted | [ bool](#bool) | Include soft-deleted ownerships. |
### ListOwnershipsResponse
(synq.owners.v1.ListOwnershipsResponse)
| Field | Type | Description |
| - | - | - |
| ownerships | [repeated Ownership](#ownership) | The owner's ownerships. |
| page\_info | [ PageInfo](#pageinfo) | Pagination cursor for the next page. |
### UpsertOwnerRequest
(synq.owners.v1.UpsertOwnerRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the owner. Accepts either the bare UUID or the `entity_id` form this API returns (`owner-`) — the prefix is stripped server-side, so the value you read back from any API can be passed straight in. The same id updates the same owner; a new id creates a new owner. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_title.title | [optional string](#string) | Owner name. Required when creating; omit to keep the existing name on update. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_contacts.contacts | [optional ContactList](#contactlist) | Notification channels. Omit to leave the existing contacts unchanged; pass a present (possibly empty) list to replace them. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_source.source | [optional Source](#source) | Source. Omit to keep the existing source (defaults to SOURCE\_SYNQ on create). |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. When set, the update fails with a conflict if the owner was modified since this etag was read. |
| actor | [ Actor](#actor) | Who is performing this write. Optional — identity is completed from the calling credentials; set it to attribute the change on behalf of a user. |
### UpsertOwnerResponse
(synq.owners.v1.UpsertOwnerResponse)
| Field | Type | Description |
| - | - | - |
| owner | [ Owner](#owner) | The stored owner after the upsert. |
### UpsertOwnershipRequest
(synq.owners.v1.UpsertOwnershipRequest)
| Field | Type | Description |
| - | - | - |
| owner\_id | [ string](#string) | Id of the owner this ownership belongs to. Accepts the bare UUID or the `owner-` entity\_id form (the prefix is stripped server-side). |
| id | [ string](#string) | Opaque id of the ownership (a UUID). The same id updates the same ownership; a new id creates a new one. |
| selection | [ OwnershipSelection](#ownershipselection) | Which assets this ownership covers. |
| alert | [ AlertConfig](#alertconfig) | How alerts fire for the covered assets. Optional. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_etag.etag | [optional string](#string) | Optional optimistic-concurrency guard. |
| actor | [ Actor](#actor) | Who is performing this write. Optional. |
### UpsertOwnershipResponse
(synq.owners.v1.UpsertOwnershipResponse)
| Field | Type | Description |
| - | - | - |
| ownership | [ Ownership](#ownership) | The stored ownership after the upsert. |
### Owner
(synq.owners.v1.Owner)
An owner: a named responsible party (a team or a person) that data assets can
be assigned to, together with the notification channels alerts about those
assets are delivered to. Assets are attached to an owner through Ownerships
(see OwnershipsService), so an owner on its own routes nothing until at least
one ownership assigns it some assets.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the owner (a UUID) supplied by the caller on create, which makes UpsertOwner idempotent. This is the id used to address the owner on this API (creating ownerships under it, deleting it). |
| entity\_id | [ string](#string) | Stable, opaque platform identifier of this owner (of the form "owner-\"). Output-only — the server derives it; never construct it by hand. This is the exact value other SYNQ APIs accept as an owner reference (for example the `synq.alerts.services.v2.AlertsService` owner path — see `synq.alerts.v2.Owner`), so read it here and pass it there to point at the same owner without needing to know how it is formed. |
| title | [ string](#string) | Human-readable name of the owner (shown as "Owner name" in the app). |
| contacts | [repeated Contact](#contact) | Notification channels for this owner. A fired alert is delivered to every contact. |
| source | [ Source](#source) | none |
| etag | [ string](#string) | Entity tag for optimistic concurrency: pass it back on UpsertOwner / DeleteOwner to guard against overwriting a concurrent change. Form: `W/"-"`. Opaque — do not parse. |
| created\_by | [ Actor](#actor) | Who created the owner. Output-only. |
| updated\_by | [ Actor](#actor) | Who last edited the owner. Output-only. |
| created\_at | [ Timestamp](#timestamp) | When the owner was created. Output-only. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | When the owner was soft-deleted, if it has been. Output-only. |
### AlertConfig
(synq.owners.v1.AlertConfig)
How alerts fire for the assets an ownership covers. Deliberately mirrors the
settings on the public alerts API (`synq.alerts.v2`) so alerting behaviour
reads the same across SYNQ.
| Field | Type | Description |
| - | - | - |
| severities | [repeated Severity](#severity) | Failure severities that fire an alert (Warning / Error / Fatal). Empty means no severity is selected and the ownership routes no failure alerts. |
| notify\_upstream | [ bool](#bool) | Also alert on issues in the upstream dependencies of the covered assets. |
| ongoing | [ OngoingAlertsStrategy](#ongoingalertsstrategy) | How repeat notifications for a still-open issue are handled: no repeats (Disabled), a scheduled digest (Schedule — a daily digest in the app), or one on every failed run (Stream). Leave unset for no ongoing notifications. |
| allow\_sql\_test\_audit\_link | [ bool](#bool) | Include a link to the SQL test audit report in notifications. |
| is\_disabled | [ bool](#bool) | When true this ownership's alerts are turned off while the assignment itself stays in place. |
### Ownership
(synq.owners.v1.Ownership)
An ownership assigns a set of data assets to an owner and configures the
alerts routed to that owner about those assets. In the app this is the
owner's "Owned assets". One owner can hold many ownerships; deleting the
owner deletes all of its ownerships.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Opaque id of the ownership (a UUID) supplied by the caller on create, which makes UpsertOwnership idempotent. This is the same value `synq.alerts.services.v2.AlertsService` reports as the ownership reference (`synq.alerts.v2.Owner.ownership_id`) on an alert attributed to this owner. |
| owner\_id | [ string](#string) | Id of the Owner this ownership belongs to (the owner's `id`, a UUID). |
| selection | [ OwnershipSelection](#ownershipselection) | Which assets this ownership covers. |
| alert | [ AlertConfig](#alertconfig) | How alerts fire for the covered assets. |
| etag | [ string](#string) | Entity tag for optimistic concurrency: pass it back on UpsertOwnership / DeleteOwnership to guard against overwriting a concurrent change. Form: `W/"-"`. Opaque — do not parse. |
| created\_by | [ Actor](#actor) | Who created the ownership. Output-only. |
| updated\_by | [ Actor](#actor) | Who last edited the ownership. Output-only. |
| created\_at | [ Timestamp](#timestamp) | When the ownership was created. Output-only. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deleted\_at.deleted\_at | [optional Timestamp](#timestamp) | When the ownership was soft-deleted, if it has been. Output-only. |
### OwnershipQuery
(synq.owners.v1.OwnershipQuery)
A named asset selection behind an ownership, expressed for the public API.
Uses the same write/read bridge as saved views and data products.
On write, provide the query in ONE of two forms:
* `public_query` — a structured query (the same shape used elsewhere for
asset selection), convenient to build from a filter UI; or
* `resolver_ql` — the resolver query language, a compact text form that is
the canonical way to author selections as code.
When both are set, `resolver_ql` wins. The server compiles and stores the
query canonically; the raw text is not preserved verbatim.
On read, the server returns `rendered_resolver_ql` (the stored query rendered
back to canonical resolver query language) and, best-effort, `public_query`
(the structured representation). `resolver_ql` is not echoed back — read
`rendered_resolver_ql`.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human-readable name of the asset group (shown as "Asset group name"). |
| resolver\_ql | [ string](#string) | WRITE (option A). The query in resolver query language. |
| rendered\_resolver\_ql | [ string](#string) | READ, output-only. The stored query rendered back to canonical resolver query language. Deterministically derived; does not preserve original hand-typed formatting or comments. |
| public\_query | [ Query](#query) | WRITE (option B) and READ. On write, the structured query to store (used when `resolver_ql` is empty). On read, a best-effort structured representation of the stored query. |
### OwnershipSelection
(synq.owners.v1.OwnershipSelection)
The set of assets an ownership covers.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.query | [ OwnershipQuery](#ownershipquery) | Assets matched by a query. Covers both a custom "asset group" and the "all assets" case (a match-all query). Unlike a data product definition (which must be a leaf), an ownership query MAY reference data products and domains — routing alerts for everything in a data product/domain is a first-class use. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) selection.dataproduct\_id | [ string](#string) | All assets belonging to a single data product, referenced by its opaque id (the `id` of a `synq.dataproducts.v2.Dataproduct`, as returned by `synq.dataproducts.v2.DataproductsService`). Accepts either the bare UUID or the `entity_id` form (`dataproduct-`) — the prefix is stripped server-side, so a product's `entity_id` from lineage can be assigned directly. The data product must exist in the workspace at write time. |
### Contact
(synq.owners.v1.Contact)
A notification channel attached to an owner. An owner can hold several
contacts of different kinds; when an owner's ownership fires an alert, the
notification is delivered to every one of the owner's contacts.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) contact\_method.slack | [ SlackChannelContact](#slackchannelcontact) | Post to a Slack channel. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) contact\_method.email | [ EmailContact](#emailcontact) | Send to a fixed set of email addresses. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) contact\_method.ms\_teams | [ MsTeamsContact](#msteamscontact) | Post to a Microsoft Teams channel. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) contact\_method.users | [ UserContact](#usercontact) | Notify Coalesce Quality users by email. |
### EmailContact
(synq.owners.v1.EmailContact)
Deliver notifications by email to a fixed list of recipients.
| Field | Type | Description |
| - | - | - |
| recipient\_emails | [repeated string](#string) | Recipient email addresses. |
### MsTeamsContact
(synq.owners.v1.MsTeamsContact)
Deliver notifications to a Microsoft Teams channel, optionally @-mentioning
members.
| Field | Type | Description |
| - | - | - |
| channel\_id | [ string](#string) | Teams channel id to post in. |
| mentions | [repeated MsTeamsMention](#msteamsmention) | Members to @-mention in the notification. Optional. |
### MsTeamsMemberMention
(synq.owners.v1.MsTeamsMemberMention)
An @-mention of an individual Microsoft Teams member.
| Field | Type | Description |
| - | - | - |
| member\_id | [ string](#string) | Teams member id that is actually mentioned. A workspace user's Teams member id is available from `synq.users.v1.UsersService` — the `msteams:` entry in `GetUserIdentities`, or an `ms_teams` alias on the user. |
| name | [ string](#string) | Display name of the member (informational). Optional. |
### MsTeamsMention
(synq.owners.v1.MsTeamsMention)
A single @-mention in a Microsoft Teams notification.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mention.member | [ MsTeamsMemberMention](#msteamsmembermention) | Mention an individual Teams member. |
### SlackChannelContact
(synq.owners.v1.SlackChannelContact)
Deliver notifications to a Slack channel, optionally @-mentioning users or
user groups in the message.
| Field | Type | Description |
| - | - | - |
| channel | [ string](#string) | Channel to post in — a channel name (e.g. "#data-alerts") or a channel id (e.g. "C0123456789"). |
| team\_name | [ string](#string) | Name of the Slack workspace (team) the channel belongs to. Optional; only needed to disambiguate when more than one Slack workspace is connected. |
| mentions | [repeated SlackMention](#slackmention) | Users and/or user groups to @-mention in the notification. Optional. |
### SlackGroupMention
(synq.owners.v1.SlackGroupMention)
An @-mention of a Slack user group.
| Field | Type | Description |
| - | - | - |
| group\_name | [ string](#string) | Display name of the group. Optional. |
| slack\_id | [ string](#string) | Slack user-group id that is actually mentioned (e.g. "S0123456789"). |
### SlackMention
(synq.owners.v1.SlackMention)
A single @-mention in a Slack notification.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mention.user | [ SlackUserMention](#slackusermention) | Mention an individual Slack user. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) mention.group | [ SlackGroupMention](#slackgroupmention) | Mention a Slack user group. |
### SlackUserMention
(synq.owners.v1.SlackUserMention)
An @-mention of an individual Slack user.
| Field | Type | Description |
| - | - | - |
| user\_name | [ string](#string) | Display handle of the user (informational; helps readers recognise the mention). Optional. |
| slack\_id | [ string](#string) | Slack user id that is actually mentioned (e.g. "U0123456789"). A workspace user's Slack id is available from `synq.users.v1.UsersService` — the `slack:` entry in `GetUserIdentities`, or a `slack` alias on the user. |
### UserContact
(synq.owners.v1.UserContact)
Deliver notifications to Coalesce Quality users, identified by email. Each
email should match a user in the workspace; the notification reaches them
through their own configured notification preferences.
Enumerate the valid users (and their emails) with `synq.users.v1.UsersService`
— `ListUsers` for the full set, or `GetByEmail` to validate a single address.
| Field | Type | Description |
| - | - | - |
| user\_emails | [repeated string](#string) | Emails of the users to notify. Each should match a workspace user; look them up via `synq.users.v1.UsersService.ListUsers` / `GetByEmail`. |
### GitContext
(synq.ingest.git.v1.GitContext)
| Field | Type | Description |
| - | - | - |
| clone\_url | [ string](#string) | none |
| branch | [ string](#string) | none |
| commit\_sha | [ string](#string) | none |
### IngestCloudwatchLogsDataRequest
(synq.ingest.cloudwatch.v1.IngestCloudwatchLogsDataRequest)
| Field | Type | Description |
| - | - | - |
| owner | [ string](#string) | none |
| log\_group | [ string](#string) | none |
| log\_stream | [ string](#string) | none |
| subscription\_filters | [repeated string](#string) | none |
| message\_type | [ string](#string) | none |
| log\_events | [repeated CloudwatchLogsLogEvent](#cloudwatchlogslogevent) | none |
### IngestCloudwatchLogsDataResponse
(synq.ingest.cloudwatch.v1.IngestCloudwatchLogsDataResponse)
### CloudwatchLogsLogEvent
(synq.ingest.cloudwatch.v1.CloudwatchLogsLogEvent)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| timestamp | [ Timestamp](#timestamp) | none |
| message | [ string](#string) | none |
### IngestExecutionRequest
(synq.ingest.sqlmesh.v1.IngestExecutionRequest)
| Field | Type | Description |
| - | - | - |
| command | [repeated string](#string) | Command passed to the sqlmesh tool |
| exit\_code | [ int32](#int32) | Exit code of the sqlmesh command |
| std\_out | [ bytes](#bytes) | Standard output log of the SqlMesh command |
| std\_err | [ bytes](#bytes) | Standard error log of the SqlMesh command |
| environment\_vars | [map EnvironmentVarsEntry](#environmentvarsentry) | Environment variables collected during execution |
| uploader\_version | [ string](#string) | Version of the uploader tool |
| uploader\_build\_time | [ string](#string) | Build time of the uploader tool |
| started\_at | [ Timestamp](#timestamp) | Time at which the execution started |
| finished\_at | [ Timestamp](#timestamp) | Time at which the execution finished |
| git\_context | [ GitContext](#gitcontext) | Git context of the project |
### IngestExecutionRequest.EnvironmentVarsEntry
(synq.ingest.sqlmesh.v1.IngestExecutionRequest.EnvironmentVarsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### IngestExecutionResponse
(synq.ingest.sqlmesh.v1.IngestExecutionResponse)
### IngestMetadataRequest
(synq.ingest.sqlmesh.v1.IngestMetadataRequest)
| Field | Type | Description |
| - | - | - |
| api\_meta | [ bytes](#bytes) | Data from /api/meta |
| models | [ bytes](#bytes) | Data from /api/models |
| model\_details | [map ModelDetailsEntry](#modeldetailsentry) | Data from /api/models/\{model\_id} |
| model\_lineage | [map ModelLineageEntry](#modellineageentry) | Data from /api/lineage/\{model\_id} |
| files | [ bytes](#bytes) | Data from /api/files |
| file\_content | [map FileContentEntry](#filecontententry) | Data from /api/file/\{file\_path} |
| environments | [ bytes](#bytes) | Data from /api/environments |
| uploader\_version | [ string](#string) | Version of the uploader tool |
| uploader\_build\_time | [ string](#string) | Build time of the uploader tool |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| git\_context | [ GitContext](#gitcontext) | Git context of the project |
| errors | [repeated Error](#error) | Optional errors indicating why some data wasn't loaded |
### IngestMetadataRequest.Error
(synq.ingest.sqlmesh.v1.IngestMetadataRequest.Error)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_path.path | [optional string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_code.code | [optional int64](#int64) | none |
| message | [ string](#string) | none |
### IngestMetadataRequest.FileContentEntry
(synq.ingest.sqlmesh.v1.IngestMetadataRequest.FileContentEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ bytes](#bytes) | none |
### IngestMetadataRequest.ModelDetailsEntry
(synq.ingest.sqlmesh.v1.IngestMetadataRequest.ModelDetailsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ bytes](#bytes) | none |
### IngestMetadataRequest.ModelLineageEntry
(synq.ingest.sqlmesh.v1.IngestMetadataRequest.ModelLineageEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ bytes](#bytes) | none |
### IngestMetadataResponse
(synq.ingest.sqlmesh.v1.IngestMetadataResponse)
### DatabasesScope
(synq.ingest.dwh.v1.DatabasesScope)
DatabasesScope makes the batch authoritative for the listed databases only.
| Field | Type | Description |
| - | - | - |
| databases | [repeated string](#string) | Each entry is "database" or "instance.database". |
### IngestLineageRequest
(synq.ingest.dwh.v1.IngestLineageRequest)
IngestLineageRequest carries one batch of lineage edges for a single
warehouse connection. Send many batches with the same connection\_id over
time; each batch is interpreted against the others according to
`replace_mode`.
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | Identifier of the warehouse connection this lineage was read from. Use the same connection\_id you use for the other Ingest\* calls of this warehouse so the lineage attaches to the right objects. |
| upload\_id | [ string](#string) | A unique id for this batch (UUID). Re-sending a batch with the same upload\_id is de-duplicated, so it is safe to retry. |
| state\_at | [ Timestamp](#timestamp) | Time at which the lineage was extracted from the warehouse. |
| source | [ LineageSource](#lineagesource) | How the lineage was derived. Drives the precedence of the resulting edges and keeps lineage of different origins from overwriting one another. |
| replace\_mode | [ LineageReplaceMode](#lineagereplacemode) | How this batch supersedes previously-ingested lineage. Defaults to MERGE (incremental) when unset. |
| scope | [ LineageScope](#lineagescope) | Required when replace\_mode is REPLACE\_SCOPE; ignored otherwise. |
| edges | [repeated LineageEdge](#lineageedge) | The lineage edges in this batch. Empty in REPLACE\_SCOPE mode is a valid way to expire all lineage inside the scope. |
### IngestLineageResponse
(synq.ingest.dwh.v1.IngestLineageResponse)
IngestLineageResponse is intentionally empty; a successful call means the
batch was accepted for processing.
### IngestObjectInformationRequest
(synq.ingest.dwh.v1.IngestObjectInformationRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| upload\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| objects | [repeated ObjectInformation](#objectinformation) | none |
### IngestObjectInformationResponse
(synq.ingest.dwh.v1.IngestObjectInformationResponse)
### IngestObjectMetricsRequest
(synq.ingest.dwh.v1.IngestObjectMetricsRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| upload\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| metrics | [repeated ObjectMetrics](#objectmetrics) | none |
### IngestObjectMetricsResponse
(synq.ingest.dwh.v1.IngestObjectMetricsResponse)
### IngestQueryLogsRequest
(synq.ingest.dwh.v1.IngestQueryLogsRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| upload\_id | [ string](#string) | none |
| query\_logs | [repeated QueryLog](#querylog) | none |
### IngestQueryLogsResponse
(synq.ingest.dwh.v1.IngestQueryLogsResponse)
### IngestSchemasRequest
(synq.ingest.dwh.v1.IngestSchemasRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| upload\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| schemas | [repeated Schema](#schema) | none |
### IngestSchemasResponse
(synq.ingest.dwh.v1.IngestSchemasResponse)
### IngestSqlDefinitionsRequest
(synq.ingest.dwh.v1.IngestSqlDefinitionsRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| upload\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| sql\_definitions | [repeated SqlDefinition](#sqldefinition) | none |
### IngestSqlDefinitionsResponse
(synq.ingest.dwh.v1.IngestSqlDefinitionsResponse)
### IngestTableConstraintsRequest
(synq.ingest.dwh.v1.IngestTableConstraintsRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| upload\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| table\_constraints | [repeated TableConstraints](#tableconstraints) | none |
### IngestTableConstraintsResponse
(synq.ingest.dwh.v1.IngestTableConstraintsResponse)
### IngestTaskResultsRequest
(synq.ingest.dwh.v1.IngestTaskResultsRequest)
| Field | Type | Description |
| - | - | - |
| connection\_id | [ string](#string) | none |
| task\_id | [ string](#string) | none |
| state\_at | [ Timestamp](#timestamp) | Time at which state was collected |
| task\_data | [ AgentTaskData](#agenttaskdata) | none |
### IngestTaskResultsResponse
(synq.ingest.dwh.v1.IngestTaskResultsResponse)
### LineageScope
(synq.ingest.dwh.v1.LineageScope)
LineageScope declares the set of downstream objects a REPLACE\_SCOPE batch is
authoritative for — the boundary inside which omitted edges are expired. The
scope is always additionally bounded by the request's connection\_id. Ignored
when the mode is MERGE.
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) scope.whole\_connection | [ WholeConnectionScope](#wholeconnectionscope) | Authoritative for the whole connection: every downstream object previously ingested under this request's connection\_id. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) scope.databases | [ DatabasesScope](#databasesscope) | Authoritative only for the listed databases. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) scope.schemas | [ SchemasScope](#schemasscope) | Authoritative only for the listed schemas. |
### SchemasScope
(synq.ingest.dwh.v1.SchemasScope)
SchemasScope makes the batch authoritative for the listed schemas only.
| Field | Type | Description |
| - | - | - |
| schemas | [repeated string](#string) | Each entry is "database.schema" or "instance.database.schema". |
### WholeConnectionScope
(synq.ingest.dwh.v1.WholeConnectionScope)
WholeConnectionScope makes the batch authoritative for the entire connection.
### LineageColumnEdge
(synq.ingest.dwh.v1.LineageColumnEdge)
LineageColumnEdge is a single column-to-column dependency carried inside a
table-level edge. Optional: omit to declare table-level lineage only. The
columns name positions on the parent LineageEdge's `upstream` and
`downstream` objects respectively.
| Field | Type | Description |
| - | - | - |
| upstream\_column | [ string](#string) | Column on the upstream (source) object. |
| downstream\_column | [ string](#string) | Column on the downstream (target) object that derives from it. |
### LineageEdge
(synq.ingest.dwh.v1.LineageEdge)
LineageEdge is one directed dependency: data flows from `upstream` into
`downstream`. Represent multi-hop lineage as the set of its direct
(distance-1) edges; do not collapse intermediate hops, so the graph stays
explicit and each hop keeps its own object domain and column edges.
| Field | Type | Description |
| - | - | - |
| upstream | [ LineageNode](#lineagenode) | Object the data flows from. |
| downstream | [ LineageNode](#lineagenode) | Object the data flows into. |
| column\_edges | [repeated LineageColumnEdge](#lineagecolumnedge) | Optional column-level dependencies within this edge. When any edge for a given downstream object carries column edges, that object's lineage is treated as column-level; otherwise it is table-level only. |
| meta | [ Struct](#struct) | Free-form provenance about how this edge was established (for example the query id, or the warehouse's own description of the deriving process). Stored as-is and not interpreted by the platform. |
### LineageNode
(synq.ingest.dwh.v1.LineageNode)
LineageNode identifies one object that participates in a lineage edge.
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | Fully-qualified name of the object. `object_name` is required; set the higher levels (instance / database / schema) to whatever the warehouse exposes so the platform can resolve the object to a known asset. |
| domain | [ LineageObjectDomain](#lineageobjectdomain) | What kind of object this is. Lets the platform separate true table-to-table lineage from edges that involve warehouse-internal objects. |
### QueryLog
(synq.ingest.dwh.v1.QueryLog)
QueryLog represents a single query execution log entry from a data warehouse.
This is the format used for ingesting query logs via the DWH agent.
The structure mirrors the internal QueryLogRawRow with protobuf serialization.
| Field | Type | Description |
| - | - | - |
| workspace | [ string](#string) | Workspace and integration identifiers (for multi-tenancy) |
| integration\_id | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_connection\_id.connection\_id | [optional string](#string) | Empty for direct connections, populated for agent uploads |
| query\_id | [ string](#string) | Query identifiers |
| created\_at | [ Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_started\_at.started\_at | [optional Timestamp](#timestamp) | Query start time (optional, uses created\_at if not set) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_finished\_at.finished\_at | [optional Timestamp](#timestamp) | Query finish time (optional, uses created\_at if not set) |
| sql | [ string](#string) | Query content |
SQL text (may be obfuscated based on sql\_obfuscation\_mode) |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_sql\_hash.sql\_hash | [optional string](#string) | SHA256 hash of original SQL for deduplication (computed during storage if not provided) |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_normalized\_query\_hash.normalized\_query\_hash | [optional string](#string) | Hash of normalized query for lineage caching (empty if not available from platform) |
\| sql\_dialect | [ string](#string) | SQL dialect (e.g., "snowflake", "bigquery", "clickhouse") |
\| query\_type | [ string](#string) | Platform-specific query type (e.g., "CREATE\_TABLE\_AS\_SELECT", "SELECT") |
\| status | [ string](#string) | Execution status: "SUCCESS", "FAILED", "CANCELED" |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_dwh\_context.dwh\_context | [optional QueryLogDwhContext](#querylogdwhcontext) | DWH execution context |
\| sql\_obfuscation\_mode | [ SqlObfuscationMode](#sqlobfuscationmode) | Obfuscation and parsing hints |
\| has\_complete\_native\_lineage | [ bool](#bool) | If true, native lineage is complete and SQL parsing can be skipped |
\| is\_truncated | [ bool](#bool) | If true, SQL was truncated by the warehouse |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_metadata.metadata | [optional Struct](#struct) | Platform-specific metadata (arbitrary key-value pairs) Contains execution metrics, costs, etc. depending on the platform |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_native\_lineage.native\_lineage | [optional QueryLogNativeLineage](#querylognativelineage) | Native lineage from the platform (if available) |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_session\_id.session\_id | [optional string](#string) | The platform's id for the unit of work the query ran in: the session, or for a statement of a BigQuery script run outside a session the script job. Queries that share one ran one after another on one connection or in one script. Empty if the platform has none. |
### QueryLogDwhContext
(synq.ingest.dwh.v1.QueryLogDwhContext)
QueryLogDwhContext represents the execution context of a query.
Contains information about where and by whom the query was executed.
Platform-specific mappings (Instance, Database, Schema):
* Snowflake: account, database\_name, schema\_name
* Databricks: workspace\_url, catalog\_name, schema\_name
* BigQuery: "", project\_id, dataset\_id
* Redshift: host, database\_name, schema\_name
* Postgres: host, database\_name, schema\_name
* Trino: host, catalog, schema
* MySQL: "", host, schema\_name
* ClickHouse: hostname, database\_name, "" (2-level: hostname.database)
* DuckDB: motherduck\_account, database\_name, schema\_name
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_instance.instance | [optional string](#string) | Instance identifier (account, workspace\_url, host, etc.) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_database.database | [optional string](#string) | Database/catalog name |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_schema.schema | [optional string](#string) | Schema name |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_warehouse.warehouse | [optional string](#string) | Warehouse identifier (Snowflake, Databricks) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_user.user | [optional string](#string) | User who executed the query |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_role.role | [optional string](#string) | Role used for execution |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_cluster.cluster | [optional string](#string) | Cluster identifier (Redshift, ClickHouse) |
### QueryLogNativeLineage
(synq.ingest.dwh.v1.QueryLogNativeLineage)
QueryLogNativeLineage contains lineage information provided natively by the data warehouse.
Not all platforms provide this - when available, it can be more accurate than SQL parsing.
| Field | Type | Description |
| - | - | - |
| input\_tables | [repeated Fqn](#fqn) | Tables read by the query |
| output\_tables | [repeated Fqn](#fqn) | Tables written to by the query |
### Fqn
(synq.ingest.dwh.v1.Fqn)
| Field | Type | Description |
| - | - | - |
| instance\_name | [ string](#string) | none |
| database\_name | [ string](#string) | none |
| schema\_name | [ string](#string) | none |
| object\_name | [ string](#string) | none |
### ObjectInformation
(synq.ingest.dwh.v1.ObjectInformation)
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | none |
| object\_native\_type | [ string](#string) | none |
| is\_view | [ bool](#bool) | none |
| is\_table | [ bool](#bool) | none |
| tags | [repeated Tag](#tag) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | none |
### ObjectMetrics
(synq.ingest.dwh.v1.ObjectMetrics)
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_row\_count.row\_count | [optional int64](#int64) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_updated\_at.updated\_at | [optional Timestamp](#timestamp) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_size\_bytes.size\_bytes | [optional int64](#int64) | none |
### Schema
(synq.ingest.dwh.v1.Schema)
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | none |
| columns | [repeated SchemaColumn](#schemacolumn) | none |
### SchemaColumn
(synq.ingest.dwh.v1.SchemaColumn)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| native\_type | [ string](#string) | none |
| ordinal\_position | [ int32](#int32) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | none |
| tags | [repeated Tag](#tag) | none |
| is\_struct | [ bool](#bool) | none |
| is\_repeated | [ bool](#bool) | none |
| fields | [repeated SchemaColumnField](#schemacolumnfield) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_precision.precision | [optional int64](#int64) | Declared precision of an exact numeric column (NUMERIC, DECIMAL, NUMBER), in decimal digits. Set whichever category the column reads as, so a NUMBER(38,0) is NUMERIC\_CATEGORY\_INTEGER with precision 38 and scale 0. Unset for every other column, and for an exact numeric whose size the data warehouse does not report. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_scale.scale | [optional int64](#int64) | Declared scale of an exact numeric column, set together with precision. Unset is not a scale of 0: it means the scale is not known. |
| numeric\_category | [ NumericCategory](#numericcategory) | What kind of number the column holds. Unspecified for a non-numeric column, and for one whose type is not recognised. When unspecified, it is read from native\_type and scale. |
### SchemaColumnField
(synq.ingest.dwh.v1.SchemaColumnField)
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | none |
| native\_type | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_description.description | [optional string](#string) | none |
| ordinal\_position | [ int32](#int32) | none |
| is\_struct | [ bool](#bool) | none |
| is\_repeated | [ bool](#bool) | none |
| fields | [repeated SchemaColumnField](#schemacolumnfield) | none |
### SqlDefinition
(synq.ingest.dwh.v1.SqlDefinition)
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | none |
| sql | [ string](#string) | none |
### TableConstraint
(synq.ingest.dwh.v1.TableConstraint)
TableConstraint represents a single constraint or index on a database table.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Name of the constraint or index |
| type | [ string](#string) | Type of the constraint (e.g. PRIMARY\_KEY, FOREIGN\_KEY, UNIQUE, CHECK, INDEX) |
| columns | [repeated string](#string) | Column names involved in this constraint or index |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_foreign\_key\_target.foreign\_key\_target | [optional Fqn](#fqn) | For foreign key: referenced table FQN |
| foreign\_key\_target\_columns | [repeated string](#string) | For foreign key: referenced column names |
| index\_type | [ string](#string) | Index type (e.g. "btree", "hash", "gin") |
| expression | [ string](#string) | Expression for check constraints or expression-based indexes |
| is\_unique | [ bool](#bool) | Whether this constraint or index enforces uniqueness |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_is\_enforced.is\_enforced | [optional bool](#bool) | Whether this constraint is enforced by the database. nil/unset = unknown, true = enforced, false = not enforced. |
### TableConstraints
(synq.ingest.dwh.v1.TableConstraints)
TableConstraints contains all constraints and indexes for a single table.
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | none |
| constraints | [repeated TableConstraint](#tableconstraint) | none |
### Tag
(synq.ingest.dwh.v1.Tag)
| Field | Type | Description |
| - | - | - |
| tag\_name | [ string](#string) | none |
| tag\_value | [ string](#string) | none |
### IngestDatahubAspectsRequest
(synq.ingest.airflow\.v1.IngestDatahubAspectsRequest)
| Field | Type | Description |
| - | - | - |
| aspects | [repeated Struct](#struct) | Request bodies of a DataHub REST emitter, each in the shape of `request`. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_action.action | [optional string](#string) | The emitter's `action` query parameter, for example `ingestProposal`. |
| request | [ Struct](#struct) | The request body of a DataHub REST emitter, for example `{"proposal": {...}}`. |
### IngestDatahubAspectsResponse
(synq.ingest.airflow\.v1.IngestDatahubAspectsResponse)
### IngestLogRequest
(synq.ingest.airflow\.v1.IngestLogRequest)
| Field | Type | Description |
| - | - | - |
| dag\_id | [ string](#string) | none |
| task\_id | [ string](#string) | none |
| run\_id | [ string](#string) | none |
| attempt | [ int32](#int32) | none |
| log | [ string](#string) | none |
| log\_time | [ Timestamp](#timestamp) | none |
### IngestLogResponse
(synq.ingest.airflow\.v1.IngestLogResponse)
### DbtArtifact
(synq.ingest.dbt.v1.DbtArtifact)
[https://docs.getdbt.com/reference/artifacts/dbt-artifacts](https://docs.getdbt.com/reference/artifacts/dbt-artifacts)
| Field | Type | Description |
| - | - | - |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) artifact.manifest\_json | [ bytes](#bytes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) artifact.run\_results\_json | [ bytes](#bytes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) artifact.catalog\_json | [ bytes](#bytes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) artifact.sources\_json | [ bytes](#bytes) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) artifact.semantic\_manifest\_json | [ bytes](#bytes) | none |
### IngestInvocationRequest
(synq.ingest.dbt.v1.IngestInvocationRequest)
| Field | Type | Description |
| - | - | - |
| args | [repeated string](#string) | Arguments passed to the dbt executable |
| exit\_code | [ int32](#int32) | Exit code of the dbt command |
| std\_out | [ bytes](#bytes) | Standard output log of the dbt command |
| std\_err | [ bytes](#bytes) | Standard error log of the dbt command |
| environment\_vars | [map EnvironmentVarsEntry](#environmentvarsentry) | Environment variables collected during execution |
| artifacts | [repeated DbtArtifact](#dbtartifact) | Artifacts generated by the dbt command |
| uploader\_version | [ string](#string) | Version of the uploader tool |
| uploader\_build\_time | [ string](#string) | Build time of the uploader tool |
| git\_context | [ GitContext](#gitcontext) | Git context of the project |
### IngestInvocationRequest.EnvironmentVarsEntry
(synq.ingest.dbt.v1.IngestInvocationRequest.EnvironmentVarsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ string](#string) | none |
### IngestInvocationResponse
(synq.ingest.dbt.v1.IngestInvocationResponse)
### IngestRequest
(synq.ingest.openlineage.v1.IngestRequest)
| Field | Type | Description |
| - | - | - |
| payload | [ Struct](#struct) | none |
### IngestResponse
(synq.ingest.openlineage.v1.IngestResponse)
### DatacheckResult
(synq.datachecks.v1.DatacheckResult)
Result from a single datacheck execution.
| Field | Type | Description |
| - | - | - |
| status | [ DatacheckStatus](#datacheckstatus) | none |
| message | [ string](#string) | none |
| sql\_test\_result | [ SqlTestResult](#sqltestresult) | none |
| monitor\_result | [ MonitorResult](#monitorresult) | none |
### MonitorPrediction
(synq.datachecks.v1.MonitorPrediction)
| Field | Type | Description |
| - | - | - |
| status | [ DatacheckStatus](#datacheckstatus) | none |
| message | [ string](#string) | none |
| value | [ double](#double) | none |
| expected | [ double](#double) | none |
| field | [ string](#string) | none |
| segment | [ string](#string) | none |
### MonitorResult
(synq.datachecks.v1.MonitorResult)
Result of a monitor execution.
| Field | Type | Description |
| - | - | - |
| monitor\_id | [ string](#string) | The monitor ID that was executed. |
| monitor\_name | [ string](#string) | none |
| monitor\_path | [ string](#string) | none |
| metrics\_version | [ int32](#int32) | none |
| entity\_id | [ Identifier](#identifier) | The entity that was monitored. |
| execution\_id | [ string](#string) | Execution ID that can be used to retrieve detailed audit information. |
| executed\_at | [ Timestamp](#timestamp) | Timestamp when the monitor was executed. |
| predictions | [repeated MonitorPrediction](#monitorprediction) | Individual segment predictions. |
### SqlTestResult
(synq.datachecks.v1.SqlTestResult)
Result of a SQL test execution.
| Field | Type | Description |
| - | - | - |
| sql\_test\_id | [ string](#string) | The SQL test ID that was executed. |
| entity\_ids | [repeated Identifier](#identifier) | The entities that the test was executed on. |
| rows\_count | [ int32](#int32) | Number of rows returned by the test query. For a passing test, this should typically be 0 (no violations found). |
| execution\_id | [ string](#string) | Execution ID that can be used to retrieve detailed audit information. |
| executed\_at | [ Timestamp](#timestamp) | Timestamp when the test was executed. |
| error\_message | [ string](#string) | Optional error message if the execution failed. |
| result\_data | [ string](#string) | Optional result data or sample rows from the test execution. |
### TriggerDatachecksRequest
(synq.datachecks.v1.TriggerDatachecksRequest)
| Field | Type | Description |
| - | - | - |
| entity\_ids | [repeated Identifier](#identifier) | List of entity identifiers to trigger datachecks for. |
### TriggerDatachecksResponse
(synq.datachecks.v1.TriggerDatachecksResponse)
| Field | Type | Description |
| - | - | - |
| results | [repeated DatacheckResult](#datacheckresult) | Results from all datacheck executions. |
### BatchDeleteSqlTestsRequest
(synq.datachecks.sqltests.v1.BatchDeleteSqlTestsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | IDs of the SqlTests to delete. |
### BatchDeleteSqlTestsResponse
(synq.datachecks.sqltests.v1.BatchDeleteSqlTestsResponse)
| Field | Type | Description |
| - | - | - |
| errors | [repeated DeleteError](#deleteerror) | IDs that were not deleted, with the reason for each. Empty when everything in the request was deleted. Every other ID in the request is deleted regardless, so a caller reconciling a set can act on what failed without retrying what did not. |
### BatchDeleteSqlTestsResponse.DeleteError
(synq.datachecks.sqltests.v1.BatchDeleteSqlTestsResponse.DeleteError)
Details of a single ID that could not be deleted.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | The ID from the request that was not deleted, echoed back as it was sent. |
| reason | [ string](#string) | Why it was not deleted. A test deployed by a SqlTestDeploymentRule cannot be deleted directly — delete or narrow the rule instead, or the rule's next sync would recreate the test. |
### BatchGetSqlTestsRequest
(synq.datachecks.sqltests.v1.BatchGetSqlTestsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | IDs of the SqlTests to fetch. |
### BatchGetSqlTestsResponse
(synq.datachecks.sqltests.v1.BatchGetSqlTestsResponse)
| Field | Type | Description |
| - | - | - |
| sql\_tests | [map SqlTestsEntry](#sqltestsentry) | none |
### BatchGetSqlTestsResponse.SqlTestsEntry
(synq.datachecks.sqltests.v1.BatchGetSqlTestsResponse.SqlTestsEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ SqlTest](#sqltest) | none |
### BatchUpsertSqlTestsRequest
(synq.datachecks.sqltests.v1.BatchUpsertSqlTestsRequest)
| Field | Type | Description |
| - | - | - |
| sql\_tests | [repeated SqlTest](#sqltest) | List of SqlTests to upsert. Each SqlTest is matched on the `id` it carries, and on its path when it has no id, so a caller that keeps its own ids can rename or re-target a test without replacing it. |
| sql\_test\_ids\_to\_reset | [repeated string](#string) | List of SqlTest IDs to re-arm, given as the same `id` sent above. A test has no learned state to discard: this re-arms the scheduler of a test that carries a recurrence rule, so it runs immediately rather than at its next slot, and does nothing for a test scheduled externally. |
### BatchUpsertSqlTestsResponse
(synq.datachecks.sqltests.v1.BatchUpsertSqlTestsResponse)
| Field | Type | Description |
| - | - | - |
| created\_ids | [repeated string](#string) | IDs for which SqlTests were created without error. |
| updated\_ids | [repeated string](#string) | IDs for which SqlTests were updated without error. |
| errors | [repeated UpsertError](#upserterror) | Errors raised while upsert. This list will be empty if there were no errors. |
### BatchUpsertSqlTestsResponse.UpsertError
(synq.datachecks.sqltests.v1.BatchUpsertSqlTestsResponse.UpsertError)
Details of errors encountered during the upsert operation.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### ConfigsFilter
(synq.datachecks.sqltests.v1.ConfigsFilter)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### GetSqlTestAuditRequest
(synq.datachecks.sqltests.v1.GetSqlTestAuditRequest)
### GetSqlTestAuditResponse
(synq.datachecks.sqltests.v1.GetSqlTestAuditResponse)
| Field | Type | Description |
| - | - | - |
| audit | [ SqlTestAudit](#sqltestaudit) | none |
### ListSqlTestsRequest
(synq.datachecks.sqltests.v1.ListSqlTestsRequest)
| Field | Type | Description |
| - | - | - |
| annotations | [repeated Annotation](#annotation) | Optional annotations to fetch SqlTests for. |
| integration\_ids | [repeated string](#string) | none |
| ids | [repeated string](#string) | none |
| config\_ids | [repeated string](#string) | none |
| configs\_filter | [ ConfigsFilter](#configsfilter) | Filter SqlTests by config IDs. Pass `ConfigsFilter{ids: ["config1", "config2"]}` to filter by specific configs. If nil, no config\_id filtering is applied. If empty, the one with NULL config\_id will be excluded. |
| sources | [repeated Source](#source) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | none |
### ListSqlTestsResponse
(synq.datachecks.sqltests.v1.ListSqlTestsResponse)
| Field | Type | Description |
| - | - | - |
| sql\_tests | [repeated SqlTest](#sqltest) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information |
### BatchDeleteSqlTestDeploymentRulesRequest
(synq.datachecks.sqltests.v1.BatchDeleteSqlTestDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | IDs of the SqlTestDeploymentRules to delete. |
### BatchDeleteSqlTestDeploymentRulesResponse
(synq.datachecks.sqltests.v1.BatchDeleteSqlTestDeploymentRulesResponse)
### BatchDeploySqlTestDeploymentRulesRequest
(synq.datachecks.sqltests.v1.BatchDeploySqlTestDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated SqlTestDeploymentRule](#sqltestdeploymentrule) | List of SqlTestDeploymentRules to deploy. |
### BatchDeploySqlTestDeploymentRulesResponse
(synq.datachecks.sqltests.v1.BatchDeploySqlTestDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deployed\_ids | [repeated string](#string) | IDs for which SqlTestDeploymentRules were deployed without error. |
| errors | [repeated DeployError](#deployerror) | Errors raised while deploying. Empty if there were no errors. |
### BatchDeploySqlTestDeploymentRulesResponse.DeployError
(synq.datachecks.sqltests.v1.BatchDeploySqlTestDeploymentRulesResponse.DeployError)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| reason | [ string](#string) | none |
### BatchGetSqlTestDeploymentRulesRequest
(synq.datachecks.sqltests.v1.BatchGetSqlTestDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
### BatchGetSqlTestDeploymentRulesResponse
(synq.datachecks.sqltests.v1.BatchGetSqlTestDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [map DeploymentRulesEntry](#deploymentrulesentry) | none |
### BatchGetSqlTestDeploymentRulesResponse.DeploymentRulesEntry
(synq.datachecks.sqltests.v1.BatchGetSqlTestDeploymentRulesResponse.DeploymentRulesEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ SqlTestDeploymentRuleView](#sqltestdeploymentruleview) | none |
### DeleteSqlTestDeploymentRuleRequest
(synq.datachecks.sqltests.v1.DeleteSqlTestDeploymentRuleRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
### DeleteSqlTestDeploymentRuleResponse
(synq.datachecks.sqltests.v1.DeleteSqlTestDeploymentRuleResponse)
### DeploySqlTestDeploymentRuleRequest
(synq.datachecks.sqltests.v1.DeploySqlTestDeploymentRuleRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rule | [ SqlTestDeploymentRule](#sqltestdeploymentrule) | none |
### DeploySqlTestDeploymentRuleResponse
(synq.datachecks.sqltests.v1.DeploySqlTestDeploymentRuleResponse)
### GetSqlTestDeployDeleteOverviewRequest
(synq.datachecks.sqltests.v1.GetSqlTestDeployDeleteOverviewRequest)
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
### GetSqlTestDeployDeleteOverviewResponse
(synq.datachecks.sqltests.v1.GetSqlTestDeployDeleteOverviewResponse)
| Field | Type | Description |
| - | - | - |
| overview | [ SqlTestsDeploymentOverview](#sqltestsdeploymentoverview) | none |
### GetSqlTestDeployOverviewRequest
(synq.datachecks.sqltests.v1.GetSqlTestDeployOverviewRequest)
| Field | Type | Description |
| - | - | - |
| deployment\_rule | [ SqlTestDeploymentRule](#sqltestdeploymentrule) | none |
### GetSqlTestDeployOverviewResponse
(synq.datachecks.sqltests.v1.GetSqlTestDeployOverviewResponse)
| Field | Type | Description |
| - | - | - |
| overview | [ SqlTestsDeploymentOverview](#sqltestsdeploymentoverview) | none |
### ListSqlTestDeploymentRulesRequest
(synq.datachecks.sqltests.v1.ListSqlTestDeploymentRulesRequest)
| Field | Type | Description |
| - | - | - |
| sources | [repeated Source](#source) | none |
| configs\_filter | [ ConfigsFilter](#configsfilter) | Filter SqlTestDeploymentRules by config IDs. Pass `ConfigsFilter{ids: ["config1", "config2"]}` to filter by specific configs. If nil, no config\_id filtering is applied. If empty, the one with NULL config\_id will be excluded. |
| rule\_ids | [repeated string](#string) | none |
| rule\_types | [repeated SqlTestDeploymentRuleType](#sqltestdeploymentruletype) | Filter by rule type. When empty, rules of every type are returned. When set, only rules whose type is listed are returned. The filter is applied server-side before paging, so every page reflects only the selected types. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_pagination.pagination | [optional Pagination](#pagination) | Pagination. Omitting this, or sending a zero page\_size, returns every matching rule in one response — there is no server-side default page size. Set page\_size to bound the response; pass page\_info.last\_id from the previous response as the cursor to fetch the next page. |
### ListSqlTestDeploymentRulesResponse
(synq.datachecks.sqltests.v1.ListSqlTestDeploymentRulesResponse)
| Field | Type | Description |
| - | - | - |
| deployment\_rules | [repeated SqlTestDeploymentRuleView](#sqltestdeploymentruleview) | none |
| page\_info | [ PageInfo](#pageinfo) | Paging information for this response. Always populated. This response carries at most one bounded page of rules, never the full set. The cursor is the paging contract: to retrieve every rule, repeat the request passing page\_info.last\_id as pagination.cursor until last\_id comes back empty. |
### AcceptedValuesTest
(synq.datachecks.sqltests.v1.AcceptedValuesTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the column to validate |
| accepted\_values | [repeated SqlValue](#sqlvalue) | List of allowed values for the column |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### BusinessQueryTest
(synq.datachecks.sqltests.v1.BusinessQueryTest)
| Field | Type | Description |
| - | - | - |
| sql\_query | [ string](#string) | Full SELECT statement. Result rows are reported as failures. |
Format: a single SELECT (CTEs allowed); no semicolon. May read any tables the integration has access to. The engine wraps it as `WITH raw_query AS () SELECT * FROM raw_query`.
Use this when the failure set requires GROUP BY, JOINs, window functions, or other shape that a per-row predicate (BusinessRuleTest) cannot express.
Example: SELECT customer\_id, SUM(amount) AS total FROM orders WHERE status = 'paid' GROUP BY customer\_id HAVING SUM(amount) \< 0
Template.identifier still anchors the test to a primary table for ownership, alerts, and UI grouping.
Placeholder: `{{ table }}` is substituted with the anchor table's fully qualified name, quoted for the warehouse — the matched table when the test is deployed by a deployment rule, which is how one query covers every table the rule matches instead of being copied onto each of them. It is the only supported placeholder; any other `{{ name }}` token is rejected.
Note: this test does not expose a `select_columns` option. The author controls the SELECT shape directly in `sql_query`. |
### BusinessRuleTest
(synq.datachecks.sqltests.v1.BusinessRuleTest)
| Field | Type | Description |
| - | - | - |
| sql\_expression | [ string](#string) | Boolean SQL predicate evaluated per row of the anchor table (Template.identifier); rows where the predicate is TRUE are reported as failures. |
Format: a single boolean expression — no SELECT, WITH, or semicolons. References columns on the anchor table. The engine executes it as `SELECT * FROM WHERE `.
Example: "total\_amount != subtotal + tax" reports rows where totals disagree. Use BusinessQueryTest for SELECT / GROUP BY / multi-table logic.
Placeholder: `{{ table }}` is substituted with the anchor table's fully qualified name, quoted for the warehouse — the matched table when the test is deployed by a deployment rule. It is the only supported placeholder; any other `{{ name }}` token is rejected. It renders a table name, so it belongs where a table name goes — the FROM clause of a subquery of your own: "total > (SELECT max(total) FROM \{\{ table }})".
It is not a column qualifier. The generated FROM clause carries no alias, and on BigQuery the fully qualified name is a single quoted identifier, so "\{\{ table }}.id" does not resolve. To name the anchor's columns, use BusinessQueryTest, where you write the FROM clause and can alias it. |
\| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### CountDistinctTest
(synq.datachecks.sqltests.v1.CountDistinctTest)
Note: this test does not expose a `select_columns` option. The result is
an aggregate count, not a set of failure rows.
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of columns to count distinct combinations |
### CountDuplicatesTest
(synq.datachecks.sqltests.v1.CountDuplicatesTest)
Note: this test does not expose a `select_columns` option. The result is
an aggregate count, not a set of failure rows.
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of columns to check for duplicate combinations |
### EmptyTest
(synq.datachecks.sqltests.v1.EmptyTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of column names to check for empty values |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### Evaluator
(synq.datachecks.sqltests.v1.Evaluator)
Evaluator is a named condition attached to a SqlTest that produces
an independent pass/fail signal. A single SqlTest may carry multiple
evaluators, each targeting a different concern (nullability, value
ranges, row counts, etc.).
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Stable identifier for the evaluator, unique within the parent SqlTest. Must contain only ASCII letters, digits, hyphens, or underscores. |
| name | [ string](#string) | Human friendly name. |
| sql\_expression | [ string](#string) | SQL expression evaluated against the test rows. The expression is a FAIL condition: a row is flagged when the expression evaluates to TRUE. NULL evaluates as pass (equivalent to FALSE). |
| severity | [ Severity](#severity) | Severity routed to alerting when this evaluator fails. Defaults to the parent SqlTest severity when left unspecified. |
### EvaluatorResult
(synq.datachecks.sqltests.v1.EvaluatorResult)
EvaluatorResult is the execution outcome of a single Evaluator within a SqlTest run.
Fields id/name/sql\_expression/severity are a snapshot of the evaluator definition
at execution time so historical results survive later edits to the test.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Evaluator id, matching Evaluator.id on the parent SqlTest. |
| name | [ string](#string) | Human-friendly evaluator name at time of execution. |
| sql\_expression | [ string](#string) | SQL expression that was evaluated. |
| severity | [ Severity](#severity) | Severity of this evaluator as configured at execution time. |
| fail\_count | [ int64](#int64) | Number of rows that failed this evaluator's condition. |
| status | [ ExecutionStatus](#executionstatus) | Execution outcome for this evaluator. |
| message | [ string](#string) | Human-readable result message (e.g. "OK" or "Got 3 failure(s)"). |
### FreshnessTest
(synq.datachecks.sqltests.v1.FreshnessTest)
| Field | Type | Description |
| - | - | - |
| time\_partition\_column\_name | [ string](#string) | Name of the timestamp column used for freshness check |
| time\_window\_seconds | [ int64](#int64) | Time window for freshness check (in seconds) |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### InputTable
(synq.datachecks.sqltests.v1.InputTable)
InputTable is a table reference resolved from the SQL expression of a SqlTest.
| Field | Type | Description |
| - | - | - |
| fqn | [ Fqn](#fqn) | Fully-qualified name of the referenced table. |
| table\_path | [ string](#string) | Synq-internal path for the table asset (e.g. "bq-prod::project::dataset::table"). |
### MaxValueTest
(synq.datachecks.sqltests.v1.MaxValueTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the numeric column to validate |
| max\_value | [ SqlValue](#sqlvalue) | Maximum allowed value (inclusive) |
| strictly | [ bool](#bool) | If true, the maximum value is exclusive |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### MinMaxTest
(synq.datachecks.sqltests.v1.MinMaxTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the numeric column to validate |
| min\_value | [ SqlValue](#sqlvalue) | Minimum allowed value (inclusive) |
| max\_value | [ SqlValue](#sqlvalue) | Maximum allowed value (inclusive) |
| strictly | [ bool](#bool) | If true, bounds are exclusive |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### MinValueTest
(synq.datachecks.sqltests.v1.MinValueTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the numeric column to validate |
| min\_value | [ SqlValue](#sqlvalue) | Minimum allowed value (inclusive) |
| strictly | [ bool](#bool) | If true, the minimum value is exclusive |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### NotNullTest
(synq.datachecks.sqltests.v1.NotNullTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of column names to check for NULL values |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### RejectedValuesTest
(synq.datachecks.sqltests.v1.RejectedValuesTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the column to validate |
| rejected\_values | [repeated SqlValue](#sqlvalue) | List of disallowed values for the column |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### RelationshipColumn
(synq.datachecks.sqltests.v1.RelationshipColumn)
| Field | Type | Description |
| - | - | - |
| source | [ string](#string) | Column name in the source table |
| reference | [ string](#string) | Column name in the reference table |
### RelationshipReference
(synq.datachecks.sqltests.v1.RelationshipReference)
| Field | Type | Description |
| - | - | - |
| entity | [ Identifier](#identifier) | The reference entity (table to check foreign keys against) |
| columns | [repeated RelationshipColumn](#relationshipcolumn) | Column mappings between source and reference tables |
### RelationshipsTest
(synq.datachecks.sqltests.v1.RelationshipsTest)
| Field | Type | Description |
| - | - | - |
| references | [repeated RelationshipReference](#relationshipreference) | List of reference table relationships to check |
| ignore\_nulls | [ bool](#bool) | If true, source rows where all join columns are NULL are excluded from results |
| time\_partition\_column | [ string](#string) | Column name used to filter source rows by time (e.g., "created\_at") |
| time\_window\_seconds | [ int64](#int64) | Time window in seconds - only check source rows within this window |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### RelativeTimeTest
(synq.datachecks.sqltests.v1.RelativeTimeTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the first time column to compare |
| relative\_column\_name | [ string](#string) | Name of the second time column that should be later than the first |
| select\_columns | [repeated string](#string) | Optional list of columns to include in the SELECT clause of the generated SQL. When empty (default), SELECT \* is used — all columns are returned. |
### SqlTest
(synq.datachecks.sqltests.v1.SqlTest)
The SqlTest is a SQL test that is executed on a Coalesce Quality entity.
| Field | Type | Description |
| - | - | - |
| platform | [ DataPlatformIdentifier](#dataplatformidentifier) | Identifier for the data platform which the SqlTest would be executed on. |
| id | [ string](#string) | Unique resource identifier for the SqlTest. This is externally maintained and can be used to fetch/update/delete this test. |
| name | [ string](#string) | Human friendly name. |
| description | [ string](#string) | Description of purpose and motivation. |
| severity | [ Severity](#severity) | none |
| sql\_expression | [ string](#string) | A valid SQL expression which is the test. |
| recurrence\_rule | [ string](#string) | [Recurrence rule](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) for the execution schedule of the SqlTest. |
| annotations | [repeated Annotation](#annotation) | Annotations for the given SqlTest.These help filter the SqlTests for later operations like listing by API, selection in UI, analytics, etc. |
| save\_failures | [ bool](#bool) | Boolean flag to to toggle saving of failure runs. |
| template | [ Template](#template) | Template for the SqlTest. required when sql\_expression is not provided |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID this test was authored from, when deployed via configuration as code. Unset for tests authored directly through this API. |
On a write this field is only ever set, never unset: omitting it keeps the stored value, so a full-replace update cannot detach the test from the config that owns it. An empty string is a real config id — the one a deploy that declares no namespace uses — so sending it attaches the test to that config rather than clearing the field. No value detaches a test; recreating it is the only way to hand it back to the app. |
\| source | [ Source](#source) | none |
\| timezone | [ string](#string) | IANA timezone name (e.g., "Europe/Prague", "America/New\_York"). Used to interpret the recurrence\_rule in the given timezone. If empty, defaults to UTC. |
\| evaluators | [repeated Evaluator](#evaluator) | Named evaluators attached to this test. Each evaluator has its own SQL boolean expression and severity. When present, the test outcome is determined by the worst severity among failing evaluators; tests with no evaluators fall back to the single-expression pass/fail rule. |
\| input\_tables | [repeated InputTable](#inputtable) | Tables referenced by the SQL expression, resolved at upsert time. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_category.category | [optional string](#string) | Technical category this test declares for itself — what kind of check it is mechanically (e.g. "nullness", "uniqueness"). It is the test's own declaration, not an administrative override: it is authoritative over any categorisation rule the workspace would otherwise apply, and is itself overridden by an explicit per-check category an admin sets.
The value is free-form and workspace-defined — whatever vocabulary your categorisation rules already use. It is displayed with underscores turned into spaces, so `snake_case` reads well.
Send the whole test on every upsert: leaving this out is the same as sending it empty, and both mean the test declares no technical category. A test then falls back to its `quality.check_category` annotation, which remains accepted and keeps working — this field simply outranks it. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_governance\_category.governance\_category | [optional string](#string) | Governance category this test declares for itself — what the check is for, the data quality dimension governance reports on (e.g. "completeness"). Resolves independently of `category` — setting one never suppresses the other — and follows the same precedence, the same free-form vocabulary and the same full-state upsert rule described above. Its annotation fallback is `quality.governance_category`. |
\| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_deployment\_rule\_id.deployment\_rule\_id | [optional string](#string) | ID of the SqlTestDeploymentRule that deployed this test, when it was created by a deployment rule rather than authored directly. OUTPUT ONLY. A test with this set is rule-managed: direct edits through this API are rejected. |
### SqlTestAudit
(synq.datachecks.sqltests.v1.SqlTestAudit)
| Field | Type | Description |
| - | - | - |
| execution\_id | [ string](#string) | none |
| rows\_count | [ int32](#int32) | none |
| result | [ string](#string) | JSON array of failing-row objects. Each object contains the original SELECT-list columns plus an `_failed_evaluators` array of evaluator IDs that flagged that row (only present when the SqlTest has evaluators). |
| created\_at | [ Timestamp](#timestamp) | none |
| evaluators | [repeated EvaluatorResult](#evaluatorresult) | Per-evaluator metadata for the run that produced this audit. Empty when the SqlTest has no evaluators. Use to resolve the evaluator IDs found in each row's `_failed_evaluators` array to their names and severities. |
| sql\_test\_path | [ string](#string) | Path of the SqlTest that produced this audit. |
| status | [ ExecutionStatus](#executionstatus) | Overall run status of the SqlTest execution that produced this audit. |
| status\_message | [ string](#string) | Short human-readable status message describing the overall run outcome. |
### SqlValue
(synq.datachecks.sqltests.v1.SqlValue)
Represents a value that can be either a literal (to be quoted in SQL) or a SQL expression (to be used as plain text).
| Field | Type | Description |
| - | - | - |
| string\_literal | [ string](#string) | Literal value that should be quoted in SQL (e.g., '2025-05-12', 'active') |
| expression | [ string](#string) | SQL expression, numbers to be used as plain text (e.g., NOW(), COUNT(\*), true, 1.1) |
### Template
(synq.datachecks.sqltests.v1.Template)
| Field | Type | Description |
| - | - | - |
| identifier | [ Identifier](#identifier) | Read-only path of matched entity. This is a unique path that can be used to access the entity in the Coalesce Quality UI. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.not\_null\_test | [ NotNullTest](#notnulltest) | Checks if specified columns contain no NULL values (e.g., user\_id should never be null) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.empty\_test | [ EmptyTest](#emptytest) | Checks if specified columns contain empty values (e.g., description field can be empty) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.accepted\_values\_test | [ AcceptedValuesTest](#acceptedvaluestest) | Validates that column values are within an allowed set (e.g., status in \['active', 'inactive', 'pending']) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.rejected\_values\_test | [ RejectedValuesTest](#rejectedvaluestest) | Validates that column values are not in a forbidden set (e.g., country not in \['UNKNOWN', 'TEST']) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.unique\_test | [ UniqueTest](#uniquetest) | Ensures specified columns form a unique key combination (e.g., order\_id is unique) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.freshness\_test | [ FreshnessTest](#freshnesstest) | Checks if data is up-to-date based on a time column (e.g., latest event\_timestamp within 24h) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.min\_max\_test | [ MinMaxTest](#minmaxtest) | Validates numeric values are within specified range (e.g., price between 0 and 1000) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.relative\_time\_test | [ RelativeTimeTest](#relativetimetest) | Compares time-based columns for logical order (e.g., start\_date before end\_date) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.business\_rule\_test | [ BusinessRuleTest](#businessruletest) | Per-row boolean predicate; rows matching are reported as failures (e.g., total\_amount != subtotal + tax) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.min\_value\_test | [ MinValueTest](#minvaluetest) | Validates numeric values are greater than or equal to a minimum value (e.g., price >= 0) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.max\_value\_test | [ MaxValueTest](#maxvaluetest) | Validates numeric values are less than or equal to a maximum value (e.g., price \<= 1000) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.relationships\_test | [ RelationshipsTest](#relationshipstest) | Validates referential integrity between source and reference tables |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.business\_query\_test | [ BusinessQueryTest](#businessquerytest) | Full SELECT whose result rows are reported as failures; supports GROUP BY, JOINs, etc. |
### UniqueTest
(synq.datachecks.sqltests.v1.UniqueTest)
Note: this test does not expose a `select_columns` option. The SELECT shape
is fixed by the aggregation (DISTINCT columns + duplicate count); failure
rows are the duplicate keys, not raw table rows.
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of columns that should form a unique combination |
| time\_partition\_column\_name | [ string](#string) | If provided, the check will be applied to the time window of the time partition column If not provided, the check will be applied to all rows |
| time\_window\_seconds | [ int64](#int64) | Time window for uniqueness check (in seconds) |
### RuleTest
(synq.datachecks.sqltests.v1.RuleTest)
RuleTest is a SQL test template deployed by a SqlTestDeploymentRule onto every
table or view the rule's selection matches. It carries the same test kinds as
SqlTest.template, but — unlike Template — has no anchor entity: the anchor is
whichever table or view the rule matched, filled in separately for each
deployed test.
| Field | Type | Description |
| - | - | - |
| name | [ string](#string) | Human friendly name for this test. Required (non-empty) for business\_rule\_test and business\_query\_test — for those two kinds, name is the test's identity key across rule redeployments, so a renamed test is treated as a different test. Optional for every other kind: leave it unset and a name is generated. |
| description | [ string](#string) | Description of purpose and motivation, copied onto every test this rule deploys. |
| severity | [ Severity](#severity) | Overrides the rule's config-level severity for this test only. Leave unset to use the config-level severity. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.not\_null\_test | [ NotNullTest](#notnulltest) | Checks if specified columns contain no NULL values (e.g., user\_id should never be null) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.empty\_test | [ EmptyTest](#emptytest) | Checks if specified columns contain empty values (e.g., description field can be empty) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.accepted\_values\_test | [ AcceptedValuesTest](#acceptedvaluestest) | Validates that column values are within an allowed set (e.g., status in \['active', 'inactive', 'pending']) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.rejected\_values\_test | [ RejectedValuesTest](#rejectedvaluestest) | Validates that column values are not in a forbidden set (e.g., country not in \['UNKNOWN', 'TEST']) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.unique\_test | [ UniqueTest](#uniquetest) | Ensures specified columns form a unique key combination (e.g., order\_id is unique) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.freshness\_test | [ FreshnessTest](#freshnesstest) | Checks if data is up-to-date based on a time column (e.g., latest event\_timestamp within 24h) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.min\_max\_test | [ MinMaxTest](#minmaxtest) | Validates numeric values are within specified range (e.g., price between 0 and 1000) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.relative\_time\_test | [ RelativeTimeTest](#relativetimetest) | Compares time-based columns for logical order (e.g., start\_date before end\_date) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.business\_rule\_test | [ BusinessRuleTest](#businessruletest) | Per-row boolean predicate; rows matching are reported as failures (e.g., total\_amount != subtotal + tax) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.min\_value\_test | [ MinValueTest](#minvaluetest) | Validates numeric values are greater than or equal to a minimum value (e.g., price ≥ 0) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.max\_value\_test | [ MaxValueTest](#maxvaluetest) | Validates numeric values are less than or equal to a maximum value (e.g., price ≤ 1000) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.relationships\_test | [ RelationshipsTest](#relationshipstest) | Validates referential integrity between source and reference tables |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.business\_query\_test | [ BusinessQueryTest](#businessquerytest) | Full SELECT whose result rows are reported as failures; supports GROUP BY, JOINs, etc. |
### SqlTestDeploymentChange
(synq.datachecks.sqltests.v1.SqlTestDeploymentChange)
SqlTestDeploymentChange describes one test's planned or applied change for a
single table or view within a SqlTestsDeploymentOverview.
| Field | Type | Description |
| - | - | - |
| table\_path | [ string](#string) | Synq path of the table or view the test targets or would target. |
| sql\_test\_path | [ string](#string) | Synq path of the SqlTest, when it already exists (create-side changes may leave this empty until the test is actually deployed). |
| test\_name | [ string](#string) | Name of the test, resolved from its RuleTest.name (or the generated name for non-business test kinds). |
| reason\_code | [ SqlTestSkipReason](#sqltestskipreason) | Why this change happens, when the reason is something other than the rule's selection. Today that is one case: SQL\_TEST\_SKIP\_REASON\_MISSING\_COLUMNS on a `tests_to_delete` entry whose table lost a column the test reads, so a caller can say "column dropped: amount" rather than reporting a bare delete. Unset (SQL\_TEST\_SKIP\_REASON\_UNSPECIFIED) otherwise, including for a delete caused by the table no longer matching the rule's selection — treat unset as "no extra reason", never as a list this change cannot appear in. |
| missing\_columns | [repeated string](#string) | Columns the table lacks, spelled as the test declares them, when `reason_code` is SQL\_TEST\_SKIP\_REASON\_MISSING\_COLUMNS. Empty otherwise. |
### SqlTestDeploymentRule
(synq.datachecks.sqltests.v1.SqlTestDeploymentRule)
SqlTestDeploymentRule dynamically deploys SQL test templates onto every table
or view its selection matches, keeping them in sync as tables and columns
change. Use it instead of authoring one SqlTest per table when the same set
of checks should apply to many tables (e.g. "not\_null(email) on every table
that has an email column").
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | Unique identifier for this rule. |
| title | [ string](#string) | Human friendly name for this rule. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.query\_config | [ SqlTestsQueryConfig](#sqltestsqueryconfig) | Deploys tests onto every match of a query; the match set is resynced over time. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_query\_config | [ SqlTestsExclusiveQueryConfig](#sqltestsexclusivequeryconfig) | Deploys tests onto every match of a query; this rule exclusively owns the match set. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.static\_config | [ SqlTestsStaticConfig](#sqltestsstaticconfig) | Deploys tests onto a single fixed table or view. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_static\_config | [ SqlTestsExclusiveStaticConfig](#sqltestsexclusivestaticconfig) | Exclusively owns a single fixed table or view. |
| source | [ Source](#source) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | Config ID this rule was authored from, when deployed via configuration as code. Unset for rules authored directly through this API. |
On a write this field is only ever set, never unset: omitting it keeps the stored value, so a full-replace update cannot detach the rule from the config that owns it. An empty string is a real config id — the one a deploy that declares no namespace uses — so sending it attaches the rule to that config rather than clearing the field. No value detaches a rule; recreating it is the only way to hand it back to the app. |
### SqlTestDeploymentRuleView
(synq.datachecks.sqltests.v1.SqlTestDeploymentRuleView)
SqlTestDeploymentRuleView is a SqlTestDeploymentRule together with a summary
of how many tests it currently has deployed.
| Field | Type | Description |
| - | - | - |
| id | [ string](#string) | none |
| title | [ string](#string) | none |
| test\_count | [ int32](#int32) | Number of tests currently deployed by this rule, across every matched table or view. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.query\_config | [ SqlTestsQueryConfig](#sqltestsqueryconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_query\_config | [ SqlTestsExclusiveQueryConfig](#sqltestsexclusivequeryconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.static\_config | [ SqlTestsStaticConfig](#sqltestsstaticconfig) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.exclusive\_static\_config | [ SqlTestsExclusiveStaticConfig](#sqltestsexclusivestaticconfig) | none |
| source | [ Source](#source) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_config\_id.config\_id | [optional string](#string) | none |
### SqlTestsDeploymentOverview
(synq.datachecks.sqltests.v1.SqlTestsDeploymentOverview)
SqlTestsDeploymentOverview previews or reports the effect of deploying a
SqlTestDeploymentRule, per test rather than per table — a table with three
tests deployed appears as three entries, one for each test.
| Field | Type | Description |
| - | - | - |
| tests\_to\_create | [repeated SqlTestDeploymentChange](#sqltestdeploymentchange) | Tests that would be newly created by this deploy. |
| tests\_to\_update | [repeated SqlTestDeploymentChange](#sqltestdeploymentchange) | Tests that already exist and would be updated (e.g. a changed severity or template) by this deploy. |
| tests\_to\_delete | [repeated SqlTestDeploymentChange](#sqltestdeploymentchange) | Tests that would be deleted by this deploy, because their table or view no longer matches the rule's selection. |
| tests\_to\_keep | [repeated SqlTestDeploymentChange](#sqltestdeploymentchange) | Tests that already exist, still match, and would be left unchanged. |
| skipped\_tables | [repeated SkippedTable](#skippedtable) | Tables or views this rule matched but could not deploy tests onto. |
| skipped\_tests | [repeated SkippedTest](#skippedtest) | Tests this rule declares that this deploy did not deploy — because another rule already owns them on the table, or because the table's columns do not support them. |
| summary | [ SqlTestsDeploymentOverviewSummary](#sqltestsdeploymentoverviewsummary) | Counts over the lists above, so a rule matching thousands of tables can be reported without reading every row. Always populated. |
### SqlTestsDeploymentOverview\.SkippedTable
(synq.datachecks.sqltests.v1.SqlTestsDeploymentOverview\.SkippedTable)
SkippedTable names a table or view the rule's selection matched, but that
this deploy could not deploy tests onto.
| Field | Type | Description |
| - | - | - |
| table\_path | [ string](#string) | Synq path of the skipped table or view. |
| reason | [ string](#string) | Why the table or view was skipped, as a stable SCREAMING\_SNAKE token: |
SKIPPED\_HIGHER\_PRIORITY\_RULE an exclusive rule has carved the table out SKIPPED\_NO\_INTEGRATION the table has no integration to run a test against SKIPPED\_TEST\_PREPARATION\_FAILED the integration's configuration is unavailable, or the table's dialect cannot render the test
Clients branch on these, so treat the spellings as part of this API: adding a value is safe, renaming one is a breaking change. A value not listed here may appear — fall through to "skipped, reason unrecognised" rather than assume the set is closed. `reason_code` is the checkable form. |
\| reason\_code | [ SqlTestSkipReason](#sqltestskipreason) | Machine-checkable form of `reason`, so a client can branch on a value the compiler knows instead of string-matching a wire constant. `reason` remains authoritative and keeps being written unchanged; `reason_code` is SQL\_TEST\_SKIP\_REASON\_UNSPECIFIED for a `reason` this enum does not yet cover. |
### SqlTestsDeploymentOverview\.SkippedTest
(synq.datachecks.sqltests.v1.SqlTestsDeploymentOverview\.SkippedTest)
SkippedTest names one test the rule declares that this deploy left in place
rather than deploying, because another rule already deployed it on that table.
Rules covering the same table merge additively: each deploys the tests that are
not there yet, and never touches a test another rule owns.
| Field | Type | Description |
| - | - | - |
| table\_path | [ string](#string) | Synq path of the table or view carrying the test. |
| sql\_test\_path | [ string](#string) | Synq path of the SqlTest, when it already exists (empty while the owning rule has not deployed it yet). |
| test\_name | [ string](#string) | Name of the test, resolved the same way as in SqlTestDeploymentChange. |
| reason | [ string](#string) | Why the test was skipped, as a stable SCREAMING\_SNAKE token: |
SKIPPED\_TEST\_OWNED\_BY\_OTHER\_RULE another rule already deploys that test SKIPPED\_MISSING\_COLUMNS the table lacks a column the test reads SKIPPED\_SCHEMA\_UNAVAILABLE no schema is known for the table yet
Clients branch on these, so treat the spellings as part of this API: adding a value is safe, renaming one is a breaking change. A value not listed here may appear — fall through to "skipped, reason unrecognised" rather than assume the set is closed. `reason_code` is the checkable form. |
\| owner\_rule\_id | [ string](#string) | Id of the deployment rule that owns the test. Set only for SKIPPED\_TEST\_OWNED\_BY\_OTHER\_RULE. |
\| owner\_rule\_title | [ string](#string) | Title the owning rule carried when this plan was made. A title can be renamed — use owner\_rule\_id to look the rule up. |
\| reason\_code | [ SqlTestSkipReason](#sqltestskipreason) | Machine-checkable form of `reason`. Same contract as SkippedTable.reason\_code: `reason` stays authoritative, and this is SQL\_TEST\_SKIP\_REASON\_UNSPECIFIED for a reason this enum does not yet cover. |
\| missing\_columns | [repeated string](#string) | Columns the test reads that the table does not have, spelled as the test declares them. Set for SKIPPED\_MISSING\_COLUMNS; empty for every other reason. A test is skipped whole — `not_null[a, b]` with only `b` missing lists just `b` but deploys neither column — so this names the gap, not the test's column list. |
\| referenced\_table\_path | [ string](#string) | Synq path of the *referenced* table, for a `relationships` test skipped because that table (rather than the table under test) lacks the column. Empty otherwise. |
### SqlTestsDeploymentOverviewSummary
(synq.datachecks.sqltests.v1.SqlTestsDeploymentOverviewSummary)
SqlTestsDeploymentOverviewSummary counts a SqlTestsDeploymentOverview, so a
caller can render "40 tables, 78 tests, 2 skipped for a missing column" without
walking lists that run to thousands of entries for a warehouse-wide rule. Every
count is derived from the lists in the same message — it adds no information a
reader of those lists could not compute, only the ability to skip reading them.
| Field | Type | Description |
| - | - | - |
| tables\_matched | [ int32](#int32) | Distinct tables and views this plan touches: every table\_path appearing in tests\_to\_create, tests\_to\_update, tests\_to\_delete, tests\_to\_keep, skipped\_tests or skipped\_tables, counted once. |
| tests\_to\_create | [ int32](#int32) | len(tests\_to\_create). |
| tests\_to\_update | [ int32](#int32) | len(tests\_to\_update). |
| tests\_to\_delete | [ int32](#int32) | len(tests\_to\_delete). |
| tests\_to\_keep | [ int32](#int32) | len(tests\_to\_keep). |
| tests\_skipped | [ int32](#int32) | len(skipped\_tests). Skipped *tables* are not counted here — see skipped\_by\_reason, which covers both lists. A test dropped because its column went away is a delete, not a skip, and counts in tests\_to\_delete. |
| skipped\_by\_reason | [map SkippedByReasonEntry](#skippedbyreasonentry) | One entry per skip across BOTH skipped\_tables and skipped\_tests, keyed by the SqlTestSkipReason value name (e.g. "SQL\_TEST\_SKIP\_REASON\_MISSING\_COLUMNS"), and "SQL\_TEST\_SKIP\_REASON\_UNSPECIFIED" for a reason string this enum does not cover. The two lists count in different units — a skipped table is one table, a skipped test is one test — so the values sum to more than `tests_skipped` whenever a table was skipped. |
| missing\_columns\_histogram | [map MissingColumnsHistogramEntry](#missingcolumnshistogramentry) | Lowercased column name to the number of tests citing it as missing, counted over every entry carrying `missing_columns` — the skipped\_tests the rule could not deploy AND the tests\_to\_delete it is removing because the column went away. Both answer the same question, "which columns does this rule not find", and a dropped column shows up as deletes rather than skips, so counting only one of the two reports an empty histogram for the case it exists for. A test missing two columns adds one to each. Lowercased so a column two tests spell differently is one entry. Empty when nothing cited a missing column. |
### SqlTestsDeploymentOverviewSummary.MissingColumnsHistogramEntry
(synq.datachecks.sqltests.v1.SqlTestsDeploymentOverviewSummary.MissingColumnsHistogramEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ int32](#int32) | none |
### SqlTestsDeploymentOverviewSummary.SkippedByReasonEntry
(synq.datachecks.sqltests.v1.SqlTestsDeploymentOverviewSummary.SkippedByReasonEntry)
| Field | Type | Description |
| - | - | - |
| key | [ string](#string) | none |
| value | [ int32](#int32) | none |
### SqlTestsExclusiveQueryConfig
(synq.datachecks.sqltests.v1.SqlTestsExclusiveQueryConfig)
SqlTestsExclusiveQueryConfig selects tables and views by query and takes
exclusive ownership of the match set: any table or view previously managed by
this rule that no longer matches has its deployed tests removed on the next
sync, with no `keep_removed_tests` opt-out (a rule managing an exclusive set
never "keeps" a table that stopped matching).
| Field | Type | Description |
| - | - | - |
| query | [ EntitySelectionQuery](#entityselectionquery) | Structured selection of tables and views this rule exclusively owns. |
| resolver\_ql | [ string](#string) | ResolverQL selection string for the exclusion set. Same semantics as SqlTestsQueryConfig.resolver\_ql: when set on write it takes precedence over `query` and is stored compiled; on read it carries the canonical rendering of the stored selection, equal to `rendered_resolver_ql`. `query` is always populated. |
| rendered\_resolver\_ql | [ string](#string) | Canonical ResolverQL rendering of the exclusion selection. OUTPUT ONLY, populated for every rule, ignored on write. |
### SqlTestsExclusiveStaticConfig
(synq.datachecks.sqltests.v1.SqlTestsExclusiveStaticConfig)
SqlTestsExclusiveStaticConfig marks a single fixed path as exclusively owned by
this rule — used to carve out a per-table override from a broader query rule
(see SqlTestDeploymentRule precedence).
| Field | Type | Description |
| - | - | - |
| path | [ string](#string) | Synq path of the table or view this rule exclusively owns. |
### SqlTestsQueryConfig
(synq.datachecks.sqltests.v1.SqlTestsQueryConfig)
SqlTestsQueryConfig selects tables and views by query, deploying every test in
`tests` onto each match. As the matched set changes over time (new tables,
dropped columns, …), a periodic sync keeps deployed tests in step.
| Field | Type | Description |
| - | - | - |
| query | [ EntitySelectionQuery](#entityselectionquery) | Structured selection of tables and views to deploy tests onto. |
| resolver\_ql | [ string](#string) | ResolverQL selection string — an advanced, more expressive alternative to `query`. ResolverQL is a superset of the structured `query`: use it for selections `query` cannot express (e.g. lineage walks, column predicates). When set (non-empty) on write, `resolver_ql` is the source of truth and the structured `query` is ignored. The selection is compiled when it is written and stored in that form, so the text itself is not kept: two spellings of the same selection store the same rule, and a type alias such as "table" is expanded to the types it names at the time of the write. On read this field carries the canonical rendering of the stored selection, equal to `rendered_resolver_ql`, whichever way the rule was written, and is empty when the selection has no ResolverQL representation. Writing a read response back unchanged stores the same selection. `query` is always populated. |
| rendered\_resolver\_ql | [ string](#string) | Canonical ResolverQL rendering of this rule's selection, populated for every rule regardless of how it was authored. OUTPUT ONLY — it is ignored on write; set `resolver_ql` to author a selection in ResolverQL. Compare selections by this field rather than by the text a rule was written with, which may be spelled differently. |
| tests | [repeated RuleTest](#ruletest) | Tests deployed onto every table or view this rule's selection matches. |
| recurrence\_rule | [ string](#string) | [Recurrence rule](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) for the execution schedule applied to every test this rule deploys. Empty means on-demand — deployed tests are never scheduled to run automatically. When set, it must carry an inline `DTSTART:` naming the first occurrence, e.g. "DTSTART:20260101T000000Z\nRRULE:FREQ=DAILY". |
| timezone | [ string](#string) | IANA timezone name (e.g., "Europe/Prague", "America/New\_York") used to interpret recurrence\_rule. Empty defaults to UTC. |
| severity | [ Severity](#severity) | Severity applied to every test this rule deploys, unless a RuleTest overrides it. Required — must be set to a value other than unspecified. |
| save\_failures | [ bool](#bool) | Applied to every test this rule deploys. Toggles saving of failure runs. |
| keep\_removed\_tests | [ bool](#bool) | When a previously matched table or view stops matching this rule's selection, its deployed tests are deleted by default. Set true to keep them as-is instead — they remain rule-managed but are no longer resynced. |
### SqlTestsStaticConfig
(synq.datachecks.sqltests.v1.SqlTestsStaticConfig)
SqlTestsStaticConfig deploys tests onto a single, fixed table or view. Unlike
the query-based configs, a static rule never resyncs against a changing
match set — it pins one path.
| Field | Type | Description |
| - | - | - |
| path | [ string](#string) | Synq path of the table or view this rule deploys tests onto. |
| tests | [repeated RuleTest](#ruletest) | Tests deployed onto the table or view at `path`. |
| recurrence\_rule | [ string](#string) | [Recurrence rule](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) for the execution schedule applied to every test this rule deploys. Empty means on-demand. When set, it must carry an inline `DTSTART:` naming the first occurrence, e.g. "DTSTART:20260101T000000Z\nRRULE:FREQ=DAILY". |
| timezone | [ string](#string) | IANA timezone name used to interpret recurrence\_rule. Empty defaults to UTC. |
| severity | [ Severity](#severity) | Severity applied to every test this rule deploys, unless a RuleTest overrides it. Required — must be set to a value other than unspecified. |
| save\_failures | [ bool](#bool) | Applied to every test this rule deploys. Toggles saving of failure runs. |
### BatchAcceptTestSuggestionsRequest
(synq.datachecks.testsuggestions.v1.BatchAcceptTestSuggestionsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
| recurrence\_rule | [ string](#string) | [Recurrence rule](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) for the execution schedule of the SqlTest. |
| annotations | [repeated Annotation](#annotation) | Annotations for the given test.These help filter the tests for later operations like listing by API, selection in UI, analytics, etc. |
| save\_failures | [ bool](#bool) | Boolean flag to to toggle saving of failure runs. |
### BatchAcceptTestSuggestionsResponse
(synq.datachecks.testsuggestions.v1.BatchAcceptTestSuggestionsResponse)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | Identifiers of created tests. |
### BatchCreateTestSuggestionsRequest
(synq.datachecks.testsuggestions.v1.BatchCreateTestSuggestionsRequest)
| Field | Type | Description |
| - | - | - |
| suggestions | [repeated TestSuggestion](#testsuggestion) | none |
### BatchCreateTestSuggestionsResponse
(synq.datachecks.testsuggestions.v1.BatchCreateTestSuggestionsResponse)
### BatchGetTestSuggestionsForEntitiesRequest
(synq.datachecks.testsuggestions.v1.BatchGetTestSuggestionsForEntitiesRequest)
| Field | Type | Description |
| - | - | - |
| identifiers | [repeated Identifier](#identifier) | none |
### BatchGetTestSuggestionsForEntitiesResponse
(synq.datachecks.testsuggestions.v1.BatchGetTestSuggestionsForEntitiesResponse)
| Field | Type | Description |
| - | - | - |
| suggestions | [repeated TestSuggestion](#testsuggestion) | none |
### BatchRejectTestSuggestionsRequest
(synq.datachecks.testsuggestions.v1.BatchRejectTestSuggestionsRequest)
| Field | Type | Description |
| - | - | - |
| ids | [repeated string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_reason.reason | [optional string](#string) | none |
### BatchRejectTestSuggestionsResponse
(synq.datachecks.testsuggestions.v1.BatchRejectTestSuggestionsResponse)
### AcceptedValuesTest
(synq.datachecks.testsuggestions.v1.AcceptedValuesTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the column to validate |
| accepted\_values | [repeated string](#string) | List of allowed values for the column |
### BusinessRuleTest
(synq.datachecks.testsuggestions.v1.BusinessRuleTest)
| Field | Type | Description |
| - | - | - |
| sql\_expression | [ string](#string) | SQL expression that should evaluate to TRUE for valid records |
### CountDistinctTest
(synq.datachecks.testsuggestions.v1.CountDistinctTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of columns to count distinct combinations |
### CountDuplicatesTest
(synq.datachecks.testsuggestions.v1.CountDuplicatesTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of columns to check for duplicate combinations |
### EmptyTest
(synq.datachecks.testsuggestions.v1.EmptyTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of column names to check for empty values |
### FreshnessTest
(synq.datachecks.testsuggestions.v1.FreshnessTest)
| Field | Type | Description |
| - | - | - |
| time\_partition\_column\_name | [ string](#string) | Name of the timestamp column used for freshness check |
| time\_window\_seconds | [ int64](#int64) | Time window for freshness check (in seconds) |
### MaxValueTest
(synq.datachecks.testsuggestions.v1.MaxValueTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the numeric column to validate |
| max\_value | [ double](#double) | Maximum allowed value (inclusive) |
| strictly | [ bool](#bool) | If true, the maximum value is exclusive |
### MinMaxTest
(synq.datachecks.testsuggestions.v1.MinMaxTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the numeric column to validate |
| min\_value | [ double](#double) | Minimum allowed value (inclusive) |
| max\_value | [ double](#double) | Maximum allowed value (inclusive) |
| strictly | [ bool](#bool) | If true, bounds are exclusive |
### MinValueTest
(synq.datachecks.testsuggestions.v1.MinValueTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the numeric column to validate |
| min\_value | [ double](#double) | Minimum allowed value (inclusive) |
| strictly | [ bool](#bool) | If true, the minimum value is exclusive |
### NotNullTest
(synq.datachecks.testsuggestions.v1.NotNullTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of column names to check for NULL values |
### RejectedValuesTest
(synq.datachecks.testsuggestions.v1.RejectedValuesTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the column to validate |
| rejected\_values | [repeated string](#string) | List of disallowed values for the column |
### RelativeTimeTest
(synq.datachecks.testsuggestions.v1.RelativeTimeTest)
| Field | Type | Description |
| - | - | - |
| column\_name | [ string](#string) | Name of the first time column to compare |
| relative\_column\_name | [ string](#string) | Name of the second time column that should be later than the first |
### RowCountTest
(synq.datachecks.testsuggestions.v1.RowCountTest)
### TestSuggestion
(synq.datachecks.testsuggestions.v1.TestSuggestion)
Test types that can be suggested
| Field | Type | Description |
| - | - | - |
| identifier | [ Identifier](#identifier) | none |
| explanation | [ string](#string) | none |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_entity\_synq\_path.entity\_synq\_path | [optional string](#string) | Read-only path of matched entity. This is a unique path that can be used to access the entity in the Coalesce Quality UI. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) \_id.id | [optional string](#string) | Read-only Coalesce Quality ID of matched entity. This is a unique ID that can be used to access the entity in the Coalesce Quality UI. |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.not\_null\_test | [ NotNullTest](#notnulltest) | Checks if specified columns contain no NULL values (e.g., user\_id should never be null) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.empty\_test | [ EmptyTest](#emptytest) | Checks if specified columns contain empty values (e.g., description field can be empty) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.accepted\_values\_test | [ AcceptedValuesTest](#acceptedvaluestest) | Validates that column values are within an allowed set (e.g., status in \['active', 'inactive', 'pending']) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.rejected\_values\_test | [ RejectedValuesTest](#rejectedvaluestest) | Validates that column values are not in a forbidden set (e.g., country not in \['UNKNOWN', 'TEST']) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.unique\_test | [ UniqueTest](#uniquetest) | Ensures specified columns form a unique key combination (e.g., order\_id is unique) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.freshness\_test | [ FreshnessTest](#freshnesstest) | Checks if data is up-to-date based on a time column (e.g., latest event\_timestamp within 24h) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.row\_count\_test | [ RowCountTest](#rowcounttest) | Deprecated: Validates the total number of rows in a table (e.g., orders table should have records) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.min\_max\_test | [ MinMaxTest](#minmaxtest) | Validates numeric values are within specified range (e.g., price between 0 and 1000) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.relative\_time\_test | [ RelativeTimeTest](#relativetimetest) | Compares time-based columns for logical order (e.g., start\_date before end\_date) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.business\_rule\_test | [ BusinessRuleTest](#businessruletest) | Custom SQL-based business logic validation (e.g., total\_amount = subtotal + tax) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.min\_value\_test | [ MinValueTest](#minvaluetest) | Validates numeric values are greater than or equal to a minimum value (e.g., price >= 0) |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) test.max\_value\_test | [ MaxValueTest](#maxvaluetest) | Validates numeric values are less than or equal to a maximum value (e.g., price \<= 1000) |
### UniqueTest
(synq.datachecks.testsuggestions.v1.UniqueTest)
| Field | Type | Description |
| - | - | - |
| column\_names | [repeated string](#string) | List of columns that should form a unique combination |
| time\_partition\_column\_name | [ string](#string) | If provided, the check will be applied to the time window of the time partition column If not provided, the check will be applied to all rows |
| time\_window\_seconds | [ int64](#int64) | Time window for uniqueness check (in seconds) |
# Enums
### LifecycleState
(synq.issues.issues.v1.LifecycleState)
Issue LifecycleState represents the current state of the issue in its life-cycle.
The state is exclusively determined as per the priority which is in the order of the fields below.
| Name | Number | Description |
| - | - | - |
| LIFECYCLE\_STATE\_UNSPECIFIED | 0 | none |
| LIFECYCLE\_STATE\_UNTRIAGED | 1 | Issue has not been triaged yet. A triaged issue is one that has been acted upon (status update or incident declaration) |
| LIFECYCLE\_STATE\_CLOSED | 2 | Issue is closed. This is a terminal state. |
| LIFECYCLE\_STATE\_ONGOING\_RESOLVED | 3 | Open issue that has been marked as resolved by the user. |
| LIFECYCLE\_STATE\_ONGOING\_DECLARED | 4 | Open issue that has been declared as an incident. |
| LIFECYCLE\_STATE\_ONGOING\_STATUS\_SET | 5 | Open issue that has status set. |
| LIFECYCLE\_STATE\_AUTOTRIAGE\_QUEUED | 11 | Open issue that has been queued for autotriage. |
| LIFECYCLE\_STATE\_AUTOTRIAGE\_ACTION\_SUGGESTED | 12 | Issue has been autotriaged and an action has been suggested. |
### Severity
(synq.issues.issues.v1.Severity)
| Name | Number | Description |
| - | - | - |
| SEVERITY\_UNSPECIFIED | 0 | none |
| SEVERITY\_INFO | 1 | none |
| SEVERITY\_WARN | 2 | none |
| SEVERITY\_ERROR | 3 | none |
| SEVERITY\_FATAL | 4 | none |
### State
(synq.issues.issues.v1.State)
Whether the issue is open / close.
| Name | Number | Description |
| - | - | - |
| STATE\_UNSPECIFIED | 0 | none |
| STATE\_OPEN | 1 | none |
| STATE\_CLOSED | 2 | none |
### Status
(synq.issues.issues.v1.Status)
Progress status of the issue.
| Name | Number | Description |
| - | - | - |
| STATUS\_UNSPECIFIED | 0 | none |
| STATUS\_INVESTIGATING | 1 | none |
| STATUS\_EXPECTED | 2 | none |
| STATUS\_FIXED | 3 | none |
| STATUS\_NO\_ACTION\_NEEDED | 4 | none |
### ListIssuesRequest.Field
(synq.issues.v2.ListIssuesRequest.Field)
| Name | Number | Description |
| - | - | - |
| FIELD\_UPDATED\_AT | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| FIELD\_STARTED\_AT | 1 | none |
### ListIssuesRequest.Sort.Order
(synq.issues.v2.ListIssuesRequest.Sort.Order)
| Name | Number | Description |
| - | - | - |
| ORDER\_ASC | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| ORDER\_DESC | 1 | none |
### Actor.Via
(synq.issues.actor.v1.Actor.Via)
| Name | Number | Description |
| - | - | - |
| VIA\_UNSPECIFIED | 0 | Unknown / not set |
| VIA\_API | 1 | Public API |
| VIA\_MCP | 2 | Official MCP server |
| VIA\_SLACK | 3 | Slack bot integration |
| VIA\_PAGERDUTY | 4 | PagerDuty webhook |
### AppDestination
(synq.recommendations.v1.AppDestination)
AppDestination is a place in the Coalesce Quality app a recommendation can
send you to. A client that does not know a value offers no button for it.
| Name | Number | Description |
| - | - | - |
| APP\_DESTINATION\_UNSPECIFIED | 0 | none |
| APP\_DESTINATION\_PROMPT\_SUGGESTIONS | 1 | The review of the AI agent's prompt suggestions. |
| APP\_DESTINATION\_SERVICE\_ACCOUNTS | 2 | The service accounts of the workspace, with the proposals waiting for review. |
### RecommendationDecisionKind
(synq.recommendations.v1.RecommendationDecisionKind)
RecommendationDecisionKind is what was decided about a recommendation.
| Name | Number | Description |
| - | - | - |
| RECOMMENDATION\_DECISION\_KIND\_UNSPECIFIED | 0 | none |
| RECOMMENDATION\_DECISION\_KIND\_DISMISSED | 1 | Hidden until something new is found. |
| RECOMMENDATION\_DECISION\_KIND\_IGNORED | 2 | Hidden for good. |
### RecommendationState
(synq.recommendations.v1.RecommendationState)
RecommendationState is whether a recommendation is in front of you.
| Name | Number | Description |
| - | - | - |
| RECOMMENDATION\_STATE\_UNSPECIFIED | 0 | none |
| RECOMMENDATION\_STATE\_OPEN | 1 | Nobody dismissed or ignored it, or something new was found since it was dismissed. |
| RECOMMENDATION\_STATE\_DISMISSED | 2 | Dismissed, and nothing new was found since: no object it did not already list, and the same remediation. An object disappearing, the message being reworded or the actions changing keep it dismissed. |
| RECOMMENDATION\_STATE\_IGNORED | 3 | Ignored. Stays hidden whatever is found later. |
### RecommendationTargetKind
(synq.recommendations.v1.RecommendationTargetKind)
RecommendationTargetKind names a kind of target, for filtering.
| Name | Number | Description |
| - | - | - |
| RECOMMENDATION\_TARGET\_KIND\_UNSPECIFIED | 0 | none |
| RECOMMENDATION\_TARGET\_KIND\_WORKSPACE | 1 | `RecommendationTarget.workspace`. |
| RECOMMENDATION\_TARGET\_KIND\_INTEGRATION | 2 | `RecommendationTarget.integration`. |
### MonitorDefinition.Source
(synq.monitors.custom\_monitors.v1.MonitorDefinition.Source)
| Name | Number | Description |
| - | - | - |
| SOURCE\_APP | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_API | 1 | none |
### Sensitivity
(synq.monitors.custom\_monitors.v1.Sensitivity)
| Name | Number | Description |
| - | - | - |
| SENSITIVITY\_UNSPECIFIED | 0 | none |
| SENSITIVITY\_PRECISE | 1 | none |
| SENSITIVITY\_BALANCED | 2 | none |
| SENSITIVITY\_RELAXED | 3 | none |
### Severity
(synq.monitors.custom\_monitors.v1.Severity)
| Name | Number | Description |
| - | - | - |
| SEVERITY\_UNSPECIFIED | 0 | none |
| SEVERITY\_WARNING | 1 | none |
| SEVERITY\_ERROR | 2 | none |
| SEVERITY\_INFO | 3 | none |
### DelayModel
(synq.monitors.v1.DelayModel)
How the expected arrival time of new data is modelled, for the delay and volume change
delay metrics. Only table stats monitors produce those metrics, so the choice has no
effect on any other monitor type.
| Name | Number | Description |
| - | - | - |
| DELAY\_MODEL\_UNSPECIFIED | 0 | Not set — the workspace default applies. |
| DELAY\_MODEL\_STANDARD | 1 | Expects data to keep arriving at the cadence it has been arriving at, regardless of which day it is. The default, and the right choice for a pipeline that runs daily. |
| DELAY\_MODEL\_WEEKLY\_PATTERN | 2 | Learns a separate cadence for each day of the week, so a pipeline that deliberately skips weekends is not reported as delayed on Saturday and Sunday. Needs roughly two weeks of history before it has a pattern to compare against. |
### DeploymentRuleType
(synq.monitors.automated\_monitors.v1.DeploymentRuleType)
DeploymentRuleType identifies which kind of selection a deployment rule uses.
The value corresponds to the populated `config` oneof arm on
MonitorsDeploymentRule / MonitorsDeploymentRuleView. Use it with
ListDeploymentRulesRequest.rule\_types to return only rules of selected kinds.
| Name | Number | Description |
| - | - | - |
| DEPLOYMENT\_RULE\_TYPE\_UNSPECIFIED | 0 | No filter — when used in a request, rules of any type are returned. |
| DEPLOYMENT\_RULE\_TYPE\_QUERY | 1 | Rule with `query_config`: selects entities by a ResolverQL query and deploys monitors on the matches. |
| DEPLOYMENT\_RULE\_TYPE\_EXCLUSIVE\_QUERY | 2 | Rule with `exclusive_query_config`: a ResolverQL query whose matches are exclusively owned (monitors outside the match set are removed). |
| DEPLOYMENT\_RULE\_TYPE\_STATIC\_PATH | 3 | Rule with `static_config`: targets a single fixed entity path. |
| DEPLOYMENT\_RULE\_TYPE\_EXCLUSIVE\_STATIC\_PATH | 4 | Rule with `exclusive_static_config`: a single fixed path, exclusively owned. |
| DEPLOYMENT\_RULE\_TYPE\_PRODUCT | 5 | Rule with `product_config`: deploys monitors across a data product's entities per its upstream/self selection strategy. |
### MetricId
(synq.monitors.automated\_monitors.v1.MetricId)
Metric a monitor predicts. ROW\_COUNT, DELAY and VOLUME\_CHANGE\_DELAY apply to table stats
monitors and deployment rules; the remaining values are field statistics and apply to
field stats columns only.
| Name | Number | Description |
| - | - | - |
| METRIC\_ID\_UNSPECIFIED | 0 | none |
| METRIC\_ID\_ROW\_COUNT | 1 | none |
| METRIC\_ID\_DELAY | 2 | none |
| METRIC\_ID\_VOLUME\_CHANGE\_DELAY | 3 | none |
| METRIC\_ID\_NUM\_ROWS | 4 | Field statistic: number of rows. |
| METRIC\_ID\_NUM\_NULL | 5 | Field statistic: number of null values. |
| METRIC\_ID\_PCT\_NULL | 6 | Field statistic: share of null values. |
| METRIC\_ID\_NUM\_UNIQUE | 7 | Field statistic: number of distinct values. |
| METRIC\_ID\_PCT\_UNIQUE | 8 | Field statistic: share of distinct values. |
| METRIC\_ID\_NUM\_EMPTY | 9 | Field statistic: number of empty values. |
| METRIC\_ID\_PCT\_EMPTY | 10 | Field statistic: share of empty values. |
| METRIC\_ID\_MEAN | 11 | Field statistic: mean value. |
| METRIC\_ID\_MIN | 12 | Field statistic: minimum value. |
| METRIC\_ID\_MAX | 13 | Field statistic: maximum value. |
| METRIC\_ID\_MEDIAN | 14 | Field statistic: median value. |
| METRIC\_ID\_STDDEV | 15 | Field statistic: standard deviation. |
### Sensitivity
(synq.monitors.automated\_monitors.v1.Sensitivity)
| Name | Number | Description |
| - | - | - |
| SENSITIVITY\_UNSPECIFIED | 0 | none |
| SENSITIVITY\_PRECISE | 1 | none |
| SENSITIVITY\_BALANCED | 2 | none |
| SENSITIVITY\_RELAXED | 3 | none |
### Severity
(synq.monitors.automated\_monitors.v1.Severity)
| Name | Number | Description |
| - | - | - |
| SEVERITY\_UNSPECIFIED | 0 | none |
| SEVERITY\_WARNING | 1 | none |
| SEVERITY\_ERROR | 2 | none |
| SEVERITY\_INFO | 3 | none |
### SkipReason
(synq.monitors.automated\_monitors.v1.SkipReason)
SkipReason is the checkable form of SkippedAsset.reason. Values mirror the
SCREAMING\_SNAKE tokens documented on that field one-for-one. The set is open
by design — a producer may report a reason before this enum has a matching
value for it — so a client must treat SKIP\_REASON\_UNSPECIFIED (and any value
this client predates) as "skipped, reason unrecognised" rather than as proof
nothing applies.
| Name | Number | Description |
| - | - | - |
| SKIP\_REASON\_UNSPECIFIED | 0 | none |
| SKIP\_REASON\_HIGHER\_PRIORITY\_RULE | 1 | none |
| SKIP\_REASON\_NO\_FRESHNESS\_COLUMN\_MATCH | 2 | none |
| SKIP\_REASON\_NO\_FRESHNESS\_FIELD\_PATTERN | 3 | none |
| SKIP\_REASON\_NO\_INTEGRATION | 4 | none |
| SKIP\_REASON\_TEST\_PREPARATION\_FAILED | 5 | none |
| SKIP\_REASON\_TEST\_OWNED\_BY\_OTHER\_RULE | 6 | none |
### SnapDirection
(synq.schedule.v1.SnapDirection)
SnapDirection constrains which way an occurrence may move to reach the grid.
| Name | Number | Description |
| - | - | - |
| SNAP\_DIRECTION\_UNSPECIFIED | 0 | Move to whichever grid point is closest. Same as NEAREST. |
| SNAP\_DIRECTION\_NEAREST | 1 | Move to whichever grid point is closest. |
| SNAP\_DIRECTION\_FORWARD | 2 | Only delay an occurrence to a later grid point, never move it earlier. |
| SNAP\_DIRECTION\_BACKWARD | 3 | Only move an occurrence earlier to a prior grid point, never delay it. |
### Status
(synq.extensions.atlan.workflows.v1.Status)
| Name | Number | Description |
| - | - | - |
| STATUS\_UNSPECIFIED | 0 | none |
| STATUS\_OK | 1 | none |
| STATUS\_FAILED | 2 | none |
### TestAlertKind
(synq.alerts.services.v2.TestAlertKind)
The alert kind a test message stands in for. It only selects the wording of
the message; the targets receive the same static test regardless.
| Name | Number | Description |
| - | - | - |
| TEST\_ALERT\_KIND\_UNSPECIFIED | 0 | Generic wording ("your alerts will be sent here"). |
| TEST\_ALERT\_KIND\_ISSUE\_LIFECYCLE | 1 | Wording for an alert on issues. |
| TEST\_ALERT\_KIND\_SCHEMA\_CHANGE | 2 | Wording for an alert on schema changes. |
| TEST\_ALERT\_KIND\_INCIDENT\_LIFECYCLE | 3 | Wording for an alert on incidents. |
### Domain.Source
(synq.domains.v2.Domain.Source)
Where this domain is managed. Domains from an external source are read-only
in the app.
| Name | Number | Description |
| - | - | - |
| SOURCE\_SYNQ | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_ATLAN | 1 | none |
### Domain.Source
(synq.domains.v1.Domain.Source)
| Name | Number | Description |
| - | - | - |
| SOURCE\_SYNQ | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_ATLAN | 1 | none |
### QueryOperand
(synq.queries.v1.QueryOperand)
QueryOperand defines how multiple QueryParts within a Query are combined.
| Name | Number | Description |
| - | - | - |
| QUERY\_OPERAND\_UNSPECIFIED | 0 | Defaults to AND behaviour. |
| QUERY\_OPERAND\_AND | 1 | Intersection: returns only entities present in ALL parts. |
| QUERY\_OPERAND\_OR | 2 | Union: returns entities present in ANY part. |
| QUERY\_OPERAND\_EXCEPT | 3 | Set difference: requires exactly 2 parts. Returns entities from parts\[0] that are NOT in parts\[1]. |
| QUERY\_OPERAND\_UPSTREAM | 4 | Lineage traversal: returns all upstream dependencies of the entities matched by the parts (combined with AND). |
| QUERY\_OPERAND\_DOWNSTREAM | 5 | Lineage traversal: returns all downstream dependents of the entities matched by the parts (combined with AND). |
### Incident.State
(synq.incidents.v1.Incident.State)
| Name | Number | Description |
| - | - | - |
| STATE\_UNSPECIFIED | 0 | none |
| STATE\_OPEN | 1 | none |
| STATE\_CLOSED | 2 | none |
| STATE\_CANCELLED | 3 | none |
### ListIncidentsRequest.Field
(synq.incidents.v1.ListIncidentsRequest.Field)
| Name | Number | Description |
| - | - | - |
| FIELD\_UNSPECIFIED | 0 | none |
| FIELD\_CREATED\_AT | 1 | none |
| FIELD\_UPDATED\_AT | 2 | none |
### ListIncidentsRequest.Sort.Order
(synq.incidents.v1.ListIncidentsRequest.Sort.Order)
| Name | Number | Description |
| - | - | - |
| ORDER\_UNSPECIFIED | 0 | none |
| ORDER\_ASC | 1 | none |
| ORDER\_DESC | 2 | none |
### CoverageFilter
(synq.overlays.v1.CoverageFilter)
CoverageFilter narrows the candidates to those in a particular coverage
state. It never widens them: everything it can return was in the selection.
| Name | Number | Description |
| - | - | - |
| COVERAGE\_FILTER\_UNSPECIFIED | 0 | Every candidate, covered or not. The full coverage table. |
| COVERAGE\_FILTER\_WITHOUT\_DATAPRODUCT | 1 | Candidates in no data product. |
| COVERAGE\_FILTER\_WITHOUT\_OWNER | 2 | Candidates with no owner. |
| COVERAGE\_FILTER\_WITHOUT\_ANY | 3 | Candidates in no data product *and* with no owner — nothing claims them at all. Narrower than either of the two above, not the union of them. |
| COVERAGE\_FILTER\_WITH\_DATAPRODUCT | 4 | Candidates in at least one data product. |
| COVERAGE\_FILTER\_WITH\_OWNER | 5 | Candidates with at least one owner. |
### Freshness
(synq.overlays.v1.Freshness)
Freshness chooses what a coverage answer is computed from.
| Name | Number | Description |
| - | - | - |
| FRESHNESS\_UNSPECIFIED | 0 | Same as `FRESHNESS_CACHED`. |
| FRESHNESS\_CACHED | 1 | The periodically recomputed membership, as everything else in this service reads it. Which overlays exist and what they are attached to is current; what each one *contains* is behind by at most one recomputation, and the response says how far in `members_computed_at`. This is what you want for anything that refreshes. |
| FRESHNESS\_LIVE | 2 | Re-resolve every overlay definition now. Correct as of the call, and far slower and far more expensive than `FRESHNESS_CACHED` — the cost is the whole catalog, not the candidate set. Use it for a one-off audit, or right after a bulk change whose effect you need to see immediately. Responses carry no `members_computed_at`, because nothing was read from an index. |
One limit worth knowing before you rely on it. This settles *whether* an asset is covered as of now; naming *which* overlays claim it still needs the recomputed membership, and that half can lag. So a row is never inconsistent with the filter that selected it — an asset this call finds uncovered comes back with empty lists whatever the index still says — but an asset that has only just started matching a rule can come back covered with nothing named yet. Counts and filters are exact either way; it is the id lists that catch up on the next recomputation. |
### ClickhouseClusterMode
(synq.integrations.v1.ClickhouseClusterMode)
| Name | Number | Description |
| - | - | - |
| CLICKHOUSE\_CLUSTER\_MODE\_UNSPECIFIED | 0 | Treated as CLICKHOUSE\_CLUSTER\_MODE\_ALL\_REPLICAS. Reading across a cluster is the behaviour of an integration that has never set this, so single-node reads have to be asked for rather than inherited from an unset field. |
| CLICKHOUSE\_CLUSTER\_MODE\_ALL\_REPLICAS | 1 | Read system tables across the named cluster, so metadata covers every replica. Requires `GRANT REMOTE ON *.*`. |
| CLICKHOUSE\_CLUSTER\_MODE\_SINGLE\_NODE | 2 | Read system tables on the connected node only. The one option that works on a ClickHouse whose remote\_servers defines no cluster, and it needs no REMOTE grant — but on a service that does have replicas it reports the metadata of a single node. |
### HealthStatus
(synq.integrations.v1.HealthStatus)
Health status of an integration, derived from its most recent runs.
| Name | Number | Description |
| - | - | - |
| HEALTH\_STATUS\_UNSPECIFIED | 0 | Unknown — no runs observed yet. |
| HEALTH\_STATUS\_OK | 1 | Last run succeeded. |
| HEALTH\_STATUS\_WARN | 2 | Last run completed with warnings. |
| HEALTH\_STATUS\_ERROR | 3 | Last run failed. |
| HEALTH\_STATUS\_CRITICAL | 4 | Last run failed critically (for example authentication or connectivity failure). |
### Scope
(synq.v1.Scope)
| Name | Number | Description |
| - | - | - |
| SCOPE\_UNSPECIFIED | 0 | none |
| SCOPE\_ISSUES\_EDIT | 10 | none |
| SCOPE\_ISSUES\_READ | 11 | none |
| SCOPE\_INCIDENTS\_EDIT | 13 | none |
| SCOPE\_INCIDENTS\_READ | 14 | none |
| SCOPE\_ENTITY\_EDIT | 30 | none |
| SCOPE\_ENTITY\_READ | 31 | none |
| SCOPE\_ENTITY\_TYPE\_EDIT | 32 | none |
| SCOPE\_ENTITY\_TYPE\_READ | 33 | none |
| SCOPE\_LINEAGE\_EDIT | 34 | none |
| SCOPE\_LINEAGE\_READ | 35 | none |
| SCOPE\_CODE\_READ | 37 | none |
| SCOPE\_STATUS\_READ | 40 | none |
| SCOPE\_INGEST\_DBT | 41 | none |
| SCOPE\_INGEST\_SQLMESH | 42 | none |
| SCOPE\_INGEST\_DWH | 43 | none |
| SCOPE\_INGEST\_SRE | 44 | none |
| SCOPE\_INGEST\_AIRFLOW | 45 | none |
| SCOPE\_INGEST\_CLOUDWATCH | 46 | none |
| SCOPE\_INGEST\_OPENLINEAGE | 47 | none |
| SCOPE\_DATACHECKS\_SQLTESTS\_EDIT | 50 | none |
| SCOPE\_DATACHECKS\_SQLTESTS\_READ | 51 | none |
| SCOPE\_DATACHECKS\_SQLTESTS\_AUDIT\_READ | 52 | none |
| SCOPE\_DATACHECKS\_TESTSUGGESTIONS\_EDIT | 53 | none |
| SCOPE\_DATACHECKS\_TESTSUGGESTIONS\_READ | 54 | none |
| SCOPE\_DATACHECKS\_TRIGGER | 55 | none |
| SCOPE\_EXECUTION\_EDIT | 60 | none |
| SCOPE\_EXECUTION\_LOG\_EDIT | 61 | none |
| SCOPE\_EXECUTION\_READ | 62 | none |
| SCOPE\_HOOK | 70 | none |
| SCOPE\_SRE\_AGENT | 71 | none |
| SCOPE\_SRE\_LLM | 72 | none |
| SCOPE\_INTEGRATION\_EDIT | 80 | none |
| SCOPE\_INTEGRATION\_READ | 81 | none |
| SCOPE\_USER\_EDIT | 90 | none |
| SCOPE\_USER\_READ | 91 | none |
| SCOPE\_USER\_ASSIGN | 92 | none |
| SCOPE\_MONITORS\_READ | 111 | none |
| SCOPE\_MONITORS\_AUTOMATIC\_EDIT | 112 | none |
| SCOPE\_MONITORS\_CUSTOM\_EDIT | 113 | none |
| SCOPE\_MONITORS\_CUSTOM\_SQL\_EDIT | 116 | none |
| SCOPE\_ALERTS\_EDIT | 130 | none |
| SCOPE\_ALERTS\_READ | 131 | none |
| SCOPE\_OWNERS\_EDIT | 140 | none |
| SCOPE\_OWNERS\_READ | 141 | none |
| SCOPE\_OWNERSHIP\_EDIT | 144 | none |
| SCOPE\_OWNERSHIP\_READ | 145 | none |
| SCOPE\_DATAPRODUCT\_EDIT | 150 | none |
| SCOPE\_DATAPRODUCT\_READ | 151 | none |
| SCOPE\_DOMAIN\_EDIT | 160 | none |
| SCOPE\_DOMAIN\_READ | 161 | none |
| SCOPE\_EXTENSIONS\_MANAGE | 180 | none |
| SCOPE\_USAGE\_GET | 192 | none |
| SCOPE\_MCP\_READ | 200 | none |
| SCOPE\_MCP\_WRITE | 201 | none |
| SCOPE\_RECON\_READ | 210 | none |
| SCOPE\_RECON\_EDIT | 211 | none |
| SCOPE\_INGEST\_RECON | 212 | none |
| SCOPE\_RECON\_PROMOTE | 213 | none |
| SCOPE\_POLICIES\_EDIT | 220 | none |
| SCOPE\_POLICIES\_READ | 221 | none |
| SCOPE\_SAVED\_VIEW\_READ | 230 | none |
| SCOPE\_SAVED\_VIEW\_EDIT | 231 | none |
| SCOPE\_SAVED\_VIEW\_ADMIN | 232 | none |
| SCOPE\_FOLDERS\_READ | 240 | none |
| SCOPE\_FOLDERS\_EDIT | 241 | none |
| SCOPE\_DELIVERY\_READ | 242 | none |
| SCOPE\_DELIVERY\_EDIT | 243 | none |
| SCOPE\_RECOMMENDATIONS\_READ | 244 | none |
| SCOPE\_RECOMMENDATIONS\_EDIT | 245 | none |
| SCOPE\_PLATFORM\_USERS\_READ | 246 | none |
| SCOPE\_PLATFORM\_USER\_LINK | 247 | none |
| SCOPE\_SERVICE\_ACCOUNTS\_READ | 248 | none |
| SCOPE\_SERVICE\_ACCOUNTS\_EDIT | 249 | none |
### Severity
(synq.v1.Severity)
| Name | Number | Description |
| - | - | - |
| SEVERITY\_UNSPECIFIED | 0 | Invalid/unspecified severity |
| SEVERITY\_WARN | 1 | Warning level failures |
| SEVERITY\_ERROR | 2 | Error level failures |
| SEVERITY\_FATAL | 3 | Fatal/critical failures |
### Source
(synq.v1.Source)
| Name | Number | Description |
| - | - | - |
| SOURCE\_APP | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_API | 1 | none |
### DataPlatformType
(synq.platforms.v1.DataPlatformType)
| Name | Number | Description |
| - | - | - |
| DATA\_PLATFORM\_TYPE\_UNSPECIFIED | 0 | none |
| DATA\_PLATFORM\_TYPE\_BIGQUERY | 10 | none |
| DATA\_PLATFORM\_TYPE\_LOOKER | 20 | none |
| DATA\_PLATFORM\_TYPE\_DBT | 30 | none |
| DATA\_PLATFORM\_TYPE\_DBT\_CLOUD | 31 | none |
| DATA\_PLATFORM\_TYPE\_DBT\_SELF\_HOSTED | 32 | none |
| DATA\_PLATFORM\_TYPE\_SNOWFLAKE | 50 | none |
| DATA\_PLATFORM\_TYPE\_GCP | 60 | none |
| DATA\_PLATFORM\_TYPE\_GIT | 70 | none |
| DATA\_PLATFORM\_TYPE\_GITHUB | 71 | none |
| DATA\_PLATFORM\_TYPE\_REDSHIFT | 80 | none |
| DATA\_PLATFORM\_TYPE\_TABLEAU | 110 | none |
| DATA\_PLATFORM\_TYPE\_AIRFLOW | 120 | none |
| DATA\_PLATFORM\_TYPE\_CLICKHOUSE | 130 | none |
| DATA\_PLATFORM\_TYPE\_POSTGRES | 160 | none |
| DATA\_PLATFORM\_TYPE\_MYSQL | 170 | none |
| DATA\_PLATFORM\_TYPE\_DATABRICKS | 180 | none |
| DATA\_PLATFORM\_TYPE\_SQLMESH | 190 | none |
| DATA\_PLATFORM\_TYPE\_DUCKDB | 200 | none |
| DATA\_PLATFORM\_TYPE\_TRINO | 210 | none |
| DATA\_PLATFORM\_TYPE\_ATLAN | 220 | none |
| DATA\_PLATFORM\_TYPE\_COALESCE | 230 | none |
| DATA\_PLATFORM\_TYPE\_CASTORDOC | 240 | none |
| DATA\_PLATFORM\_TYPE\_FIVETRAN | 250 | none |
| DATA\_PLATFORM\_TYPE\_DAGSTER | 260 | none |
| DATA\_PLATFORM\_TYPE\_AIRBYTE | 270 | none |
| DATA\_PLATFORM\_TYPE\_STITCH | 280 | none |
| DATA\_PLATFORM\_TYPE\_SNOWPLOW | 290 | none |
| DATA\_PLATFORM\_TYPE\_POWERBI | 300 | none |
| DATA\_PLATFORM\_TYPE\_THOUGHTSPOT | 310 | none |
| DATA\_PLATFORM\_TYPE\_SIGMA | 320 | none |
| DATA\_PLATFORM\_TYPE\_DOMO | 330 | none |
| DATA\_PLATFORM\_TYPE\_MSSQL | 340 | none |
| DATA\_PLATFORM\_TYPE\_ORACLE | 350 | none |
| DATA\_PLATFORM\_TYPE\_ATHENA | 360 | none |
| DATA\_PLATFORM\_TYPE\_FABRIC | 370 | none |
| DATA\_PLATFORM\_TYPE\_DB2 | 380 | none |
| DATA\_PLATFORM\_TYPE\_LOOKER\_STUDIO | 390 | none |
| DATA\_PLATFORM\_TYPE\_HEX | 400 | none |
| DATA\_PLATFORM\_TYPE\_OMNI | 410 | none |
| DATA\_PLATFORM\_TYPE\_METABASE | 420 | none |
| DATA\_PLATFORM\_TYPE\_GOOGLE\_SHEETS | 430 | none |
| DATA\_PLATFORM\_TYPE\_GRAFANA | 440 | none |
| DATA\_PLATFORM\_TYPE\_STREAMLIT | 450 | none |
| DATA\_PLATFORM\_TYPE\_ASTRATO | 460 | none |
| DATA\_PLATFORM\_TYPE\_SNOWFLAKE\_CORTEX | 470 | none |
| DATA\_PLATFORM\_TYPE\_SNOWFLAKE\_INTELLIGENCE | 480 | none |
| DATA\_PLATFORM\_TYPE\_CUBE | 490 | none |
| DATA\_PLATFORM\_TYPE\_CENSUS | 500 | none |
| DATA\_PLATFORM\_TYPE\_HIGHTOUCH | 510 | none |
| DATA\_PLATFORM\_TYPE\_WORKATO | 520 | none |
| DATA\_PLATFORM\_TYPE\_ESTUARY | 530 | none |
| DATA\_PLATFORM\_TYPE\_DLT | 540 | none |
| DATA\_PLATFORM\_TYPE\_TERRAFORM | 550 | none |
| DATA\_PLATFORM\_TYPE\_HEVO | 560 | none |
| DATA\_PLATFORM\_TYPE\_DATASTREAM | 570 | none |
| DATA\_PLATFORM\_TYPE\_SYNQ | 1000 | none |
### Event.EventType
(synq.webhooks.v1.Event.EventType)
| Name | Number | Description |
| - | - | - |
| EVENT\_TYPE\_UNSPECIFIED | 0 | none |
| EVENT\_TYPE\_PING | 10 | none |
| EVENT\_TYPE\_ISSUE\_CREATED | 11 | none |
| EVENT\_TYPE\_ISSUE\_UPDATED | 12 | none |
| EVENT\_TYPE\_ISSUE\_STATUS\_UPDATED | 13 | none |
| EVENT\_TYPE\_ISSUE\_CLOSED | 14 | none |
| EVENT\_TYPE\_INCIDENT\_OPEN | 20 | none |
| EVENT\_TYPE\_INCIDENT\_CLOSED | 21 | none |
| EVENT\_TYPE\_INCIDENT\_CANCELLED | 22 | none |
### IssueSummary.IssueStatus
(synq.webhooks.v1.IssueSummary.IssueStatus)
| Name | Number | Description |
| - | - | - |
| ISSUE\_STATUS\_UNSPECIFIED | 0 | none |
| ISSUE\_STATUS\_INVESTIGATING | 1 | none |
| ISSUE\_STATUS\_EXPECTED | 2 | none |
| ISSUE\_STATUS\_FIXED | 3 | none |
| ISSUE\_STATUS\_NO\_ACTION\_NEEDED | 4 | none |
### SkipReason
(synq.deliveries.v1.SkipReason)
SkipReason says why a delivery sent nothing. Every code path that declines to
send names one, so an integration that appears silent can be explained rather
than guessed at.
It is an enum rather than a message we wrote once, so a caller can act on the
reason: a not-subscribed event is fixed in the integration's settings, a
disabled integration is fixed by enabling it, and an unsupported event type is
not fixable at all.
| Name | Number | Description |
| - | - | - |
| SKIP\_REASON\_UNSPECIFIED | 0 | none |
| SKIP\_REASON\_EVENT\_TYPE\_NOT\_SUBSCRIBED | 1 | The event type is not among the ones the integration subscribes to. |
| SKIP\_REASON\_EVENT\_TYPE\_UNSUPPORTED\_BY\_INTEGRATION | 2 | The destination has no representation for this kind of event. |
| SKIP\_REASON\_NO\_ACTION\_FOR\_LIFECYCLE\_STATE | 3 | The change of state maps to no action on the destination. |
| SKIP\_REASON\_INTEGRATION\_NOT\_FOUND | 4 | The integration the event was addressed to no longer exists. |
| SKIP\_REASON\_INTEGRATION\_DISABLED | 5 | The integration exists but is disabled. |
| SKIP\_REASON\_EMPTY\_EVENT\_SUBJECT | 6 | The event carried no issue or incident to describe. |
### StatusClass
(synq.deliveries.v1.StatusClass)
StatusClass groups HTTP statuses the way a delivery report reads them.
| Name | Number | Description |
| - | - | - |
| STATUS\_CLASS\_UNSPECIFIED | 0 | none |
| STATUS\_CLASS\_SUCCESS | 2 | 2xx — accepted. The only class that counts as delivered. |
| STATUS\_CLASS\_REDIRECT | 3 | 3xx. Redirects are not followed: a delivery carries credentials scoped to one host, so a destination answering with one is treated as not having accepted the payload. |
| STATUS\_CLASS\_CLIENT\_ERROR | 4 | 4xx — the destination rejected the request. |
| STATUS\_CLASS\_SERVER\_ERROR | 5 | 5xx — the destination failed to handle it. |
| STATUS\_CLASS\_NO\_RESPONSE | 6 | No response arrived at all: a connection failure, a TLS error, a timeout. |
### Outcome
(synq.deliveries.v1.Outcome)
Outcome is what became of a delivery. It is a state the service maintains,
not something to recompute from status codes: a 500 that will be retried and
a 500 that exhausted the retries are the same status and different outcomes.
| Name | Number | Description |
| - | - | - |
| OUTCOME\_UNSPECIFIED | 0 | none |
| OUTCOME\_PENDING | 1 | Accepted and queued, with at least one attempt still to come. |
| OUTCOME\_DELIVERED | 2 | Every request the delivery produced was accepted by the destination. |
| OUTCOME\_FAILED | 3 | The destination rejected it, or it was still failing when the retries ran out. No further attempt will be made — use `Redeliver` to try again. |
| OUTCOME\_SKIPPED | 4 | The integration was reached and declined to send anything; `skip_reason` says why. Nothing was attempted and nothing will be. |
| OUTCOME\_NOT\_ROUTED | 5 | No integration could be resolved for the event at all; `skip_reason` says why. Distinct from `OUTCOME_SKIPPED`, where the integration was found and it was its own configuration that declined. |
### SubjectType
(synq.deliveries.v1.SubjectType)
| Name | Number | Description |
| - | - | - |
| SUBJECT\_TYPE\_UNSPECIFIED | 0 | none |
| SUBJECT\_TYPE\_ISSUE | 1 | none |
| SUBJECT\_TYPE\_INCIDENT | 2 | none |
| SUBJECT\_TYPE\_DELIVERY\_TEST | 3 | A delivery sent by `SendTestDelivery`, which is about no issue or incident. |
### IssueTriageStatus
(synq.agent.sre.v1.IssueTriageStatus)
IssueTriageStatus represents the current stage of an issue in the SRE agent's investigation workflow.
The status progresses as the agent collects evidence and reaches conclusions about the issue.
| Name | Number | Description |
| - | - | - |
| ISSUE\_TRIAGE\_STATUS\_UNSPECIFIED | 0 | Default value, should not be used |
| ISSUE\_TRIAGE\_STATUS\_SCHEDULED | 1 | Issue has been queued for investigation but the SRE agent has not started processing it yet |
| ISSUE\_TRIAGE\_STATUS\_IN\_PROGRESS | 2 | SRE agent is actively investigating the issue, collecting evidence and analyzing the situation |
| ISSUE\_TRIAGE\_STATUS\_CANCELLED | 7 | Issue has been scheduled but later cancelled |
| ISSUE\_TRIAGE\_STATUS\_FAILED | 8 | Issue wasn't triaged as such process failed. Scheduling again is possible. |
| ISSUE\_TRIAGE\_STATUS\_TRIAGED | 3 | SRE agent has generated conclusion for the issue |
| ISSUE\_TRIAGE\_STATUS\_ACCEPTED | 4 | User accepted the issue conclusion SRE agent has determined |
| ISSUE\_TRIAGE\_STATUS\_IGNORED | 5 | User Discarded the issue conclusion SRE agent has determined |
| ISSUE\_TRIAGE\_STATUS\_CLOSED | 6 | Issue was closed by the system |
### TriageFailureCategory
(synq.agent.sre.v1.TriageFailureCategory)
TriageFailureCategory classifies why a triage attempt failed.
The frontend can use this to render appropriate user-facing messages and actions.
| Name | Number | Description |
| - | - | - |
| TRIAGE\_FAILURE\_CATEGORY\_UNSPECIFIED | 0 | Default value, should not be used |
| TRIAGE\_FAILURE\_CATEGORY\_BUDGET\_EXCEEDED | 1 | Daily AI budget limit reached, triage will resume automatically the next day |
| TRIAGE\_FAILURE\_CATEGORY\_CONTEXT\_TOO\_LARGE | 2 | Issue context is too large for the AI agent to process |
| TRIAGE\_FAILURE\_CATEGORY\_MAX\_ITERATIONS | 3 | AI agent couldn't reach a conclusion within the allowed number of steps |
| TRIAGE\_FAILURE\_CATEGORY\_INVALID\_AGENT\_OUTPUT | 4 | AI agent produced an invalid or unparseable response |
| TRIAGE\_FAILURE\_CATEGORY\_CONNECTIVITY | 5 | Temporary connectivity issue to upstream services |
| TRIAGE\_FAILURE\_CATEGORY\_RESOURCE\_NOT\_FOUND | 6 | Referenced resource (e.g. monitor) no longer exists |
| TRIAGE\_FAILURE\_CATEGORY\_INTERNAL | 7 | An unexpected internal error occurred |
| TRIAGE\_FAILURE\_CATEGORY\_NOT\_CONFIGURED | 8 | Scout is not enabled for this workspace, so no triage can run until an administrator configures it |
### TestSuggestionStatus
(synq.agent.sre.v1.TestSuggestionStatus)
| Name | Number | Description |
| - | - | - |
| TEST\_SUGGESTION\_STATUS\_UNSPECIFIED | 0 | none |
| TEST\_SUGGESTION\_STATUS\_SCHEDULED | 1 | none |
| TEST\_SUGGESTION\_STATUS\_RUNNING | 2 | none |
| TEST\_SUGGESTION\_STATUS\_COMPLETED | 3 | none |
| TEST\_SUGGESTION\_STATUS\_FAILED | 4 | none |
| TEST\_SUGGESTION\_STATUS\_CANCELLED | 5 | none |
### MessageRole
(synq.agent.sre.v1.MessageRole)
Role of the message in the conversation.
| Name | Number | Description |
| - | - | - |
| MESSAGE\_ROLE\_UNSPECIFIED | 0 | none |
| MESSAGE\_ROLE\_USER | 1 | Message from the user/human. |
| MESSAGE\_ROLE\_ASSISTANT | 2 | Message from the assistant/LLM. |
### ModelType
(synq.agent.sre.v1.ModelType)
Type of model to use for the evaluation.
| Name | Number | Description |
| - | - | - |
| MODEL\_TYPE\_UNSPECIFIED | 0 | none |
| MODEL\_TYPE\_SUMMARY | 1 | Summary model - faster and cheaper, suitable for simple tasks. This is the default when not specified. |
| MODEL\_TYPE\_THINKING | 2 | Thinking model - more capable, suitable for complex reasoning tasks. |
### PromptField
(synq.agent.sre.v1.PromptField)
PromptField identifies which SRE agent system-prompt a suggestion targets.
Mirrors the four `*_system_prompt` fields returned by `GetConfig`.
| Name | Number | Description |
| - | - | - |
| PROMPT\_FIELD\_UNSPECIFIED | 0 | none |
| PROMPT\_FIELD\_TRIAGE | 1 | none |
| PROMPT\_FIELD\_TESTER | 2 | none |
| PROMPT\_FIELD\_WORKSPACE | 3 | none |
| PROMPT\_FIELD\_ISSUE\_GROUPING | 4 | none |
### PromptSuggestionStatus
(synq.agent.sre.v1.PromptSuggestionStatus)
PromptSuggestionStatus is the review lifecycle of a single prompt suggestion.
`SUPERSEDED` indicates a newer pending suggestion for the same field replaced
this one before it was reviewed.
| Name | Number | Description |
| - | - | - |
| PROMPT\_SUGGESTION\_STATUS\_UNSPECIFIED | 0 | none |
| PROMPT\_SUGGESTION\_STATUS\_PENDING | 1 | none |
| PROMPT\_SUGGESTION\_STATUS\_ACCEPTED | 2 | none |
| PROMPT\_SUGGESTION\_STATUS\_DISMISSED | 3 | none |
| PROMPT\_SUGGESTION\_STATUS\_SUPERSEDED | 4 | none |
### LogLevel
(synq.agent.dwh.v1.LogLevel)
| Name | Number | Description |
| - | - | - |
| LOG\_LEVEL\_UNSPECIFIED | 0 | none |
| LOG\_LEVEL\_INFO | 1 | none |
| LOG\_LEVEL\_WARN | 2 | none |
| LOG\_LEVEL\_ERROR | 3 | none |
### ClickhouseClusterMode
(synq.agent.dwh.v1.ClickhouseClusterMode)
| Name | Number | Description |
| - | - | - |
| CLICKHOUSE\_CLUSTER\_MODE\_UNSPECIFIED | 0 | Treated as CLICKHOUSE\_CLUSTER\_MODE\_ALL\_REPLICAS. Reading across a cluster is the behaviour of a connection that has never set this, so single-node reads have to be asked for rather than inherited from an unset field. |
| CLICKHOUSE\_CLUSTER\_MODE\_ALL\_REPLICAS | 1 | Read system tables across the named cluster, so metadata covers every replica. Requires `GRANT REMOTE ON *.*`. |
| CLICKHOUSE\_CLUSTER\_MODE\_SINGLE\_NODE | 2 | Read system tables on the connected node only. The one option that works on a ClickHouse whose remote\_servers defines no cluster, and it needs no REMOTE grant — but on a service that does have replicas it reports the metadata of a single node. |
### Config.Agent.LogLevel
(synq.agent.dwh.v1.Config.Agent.LogLevel)
| Name | Number | Description |
| - | - | - |
| LOG\_LEVEL\_UNSPECIFIED | 0 | Defaults to info |
| LOG\_LEVEL\_TRACE | 1 | none |
| LOG\_LEVEL\_DEBUG | 2 | none |
| LOG\_LEVEL\_INFO | 3 | none |
| LOG\_LEVEL\_WARN | 4 | none |
| LOG\_LEVEL\_ERROR | 5 | none |
### CutoffAggregateFunction
(synq.agent.recon.v1.CutoffAggregateFunction)
CutoffAggregateFunction defines the aggregate function for watermark derivation.
| Name | Number | Description |
| - | - | - |
| CUTOFF\_AGGREGATE\_FUNCTION\_UNSPECIFIED | 0 | Default: MAX. |
| CUTOFF\_AGGREGATE\_FUNCTION\_MAX | 1 | none |
| CUTOFF\_AGGREGATE\_FUNCTION\_MIN | 2 | none |
### CutoffCombineStrategy
(synq.agent.recon.v1.CutoffCombineStrategy)
CutoffCombineStrategy defines how to combine watermarks from source and target.
| Name | Number | Description |
| - | - | - |
| CUTOFF\_COMBINE\_STRATEGY\_UNSPECIFIED | 0 | Default: MIN. |
| CUTOFF\_COMBINE\_STRATEGY\_MIN | 1 | Use the smaller of source and target watermarks (safe default). |
| CUTOFF\_COMBINE\_STRATEGY\_MAX | 2 | Use the larger of source and target watermarks. |
| CUTOFF\_COMBINE\_STRATEGY\_SOURCE | 3 | Use only the source watermark (target must not be configured). |
| CUTOFF\_COMBINE\_STRATEGY\_TARGET | 4 | Use only the target watermark (source must not be configured). |
### CutoffTruncateUnit
(synq.agent.recon.v1.CutoffTruncateUnit)
CutoffTruncateUnit defines time truncation for the cutoff value.
| Name | Number | Description |
| - | - | - |
| CUTOFF\_TRUNCATE\_UNIT\_UNSPECIFIED | 0 | No truncation (default). |
| CUTOFF\_TRUNCATE\_UNIT\_HOUR | 1 | none |
| CUTOFF\_TRUNCATE\_UNIT\_DAY | 2 | none |
| CUTOFF\_TRUNCATE\_UNIT\_WEEK | 3 | none |
| CUTOFF\_TRUNCATE\_UNIT\_MONTH | 4 | none |
| CUTOFF\_TRUNCATE\_UNIT\_QUARTER | 5 | none |
| CUTOFF\_TRUNCATE\_UNIT\_YEAR | 6 | none |
### AggregateFunction
(synq.agent.recon.v1.AggregateFunction)
AggregateFunction defines the supported SQL aggregate functions
for measure comparisons in aggregate reconciliation mode.
| Name | Number | Description |
| - | - | - |
| AGGREGATE\_FUNCTION\_UNSPECIFIED | 0 | none |
| AGGREGATE\_FUNCTION\_SUM | 1 | SQL SUM() — total of all values in the group. |
| AGGREGATE\_FUNCTION\_COUNT | 2 | SQL COUNT() — number of rows in the group. |
| AGGREGATE\_FUNCTION\_AVG | 3 | SQL AVG() — arithmetic mean of values in the group. |
| AGGREGATE\_FUNCTION\_MIN | 4 | SQL MIN() — smallest value in the group. |
| AGGREGATE\_FUNCTION\_MAX | 5 | SQL MAX() — largest value in the group. |
### PercentageBasis
(synq.agent.recon.v1.PercentageBasis)
PercentageBasis controls which percentage formula is used for threshold comparison.
The choice affects how "big" a difference appears when source and target values
differ in magnitude.
| Name | Number | Description | | | | | | |
| - | - | - | - | - | - | - | - | - |
| PERCENTAGE\_BASIS\_UNSPECIFIED | 0 | Default: source. | | | | | | |
| PERCENTAGE\_BASIS\_SOURCE | 1 | | diff | / | source | — "target is X% off from source." Best when source is the authoritative baseline. | | |
| PERCENTAGE\_BASIS\_TARGET | 2 | | diff | / | target | — "source is X% off from target." Best when target is the authoritative baseline. | | |
| PERCENTAGE\_BASIS\_SYMMETRIC | 3 | 2 \* | diff | / ( | source | + | target | ) — symmetric, neither side is privileged. Best when both sides are equally authoritative. |
### ResumeMode
(synq.agent.recon.v1.ResumeMode)
ResumeMode selects which replay a ResumeFromRun performs.
| Name | Number | Description |
| - | - | - |
| RESUME\_MODE\_UNSPECIFIED | 0 | none |
| RESUME\_MODE\_RECHECK | 1 | Re-run the parent's queries and report what changed: fixed, still mismatched, regressed or unchanged. |
| RESUME\_MODE\_DRILL\_DEEPER | 2 | Continue the parent's drill from where it stopped — the mismatch segments a bisection left open, or the divergent groups an aggregate found — instead of re-drilling the whole dataset from the top. |
### IngestMessageSeverity
(synq.agent.recon.v1.IngestMessageSeverity)
IngestMessageSeverity is how prominently to show an IngestMessage.
| Name | Number | Description |
| - | - | - |
| INGEST\_MESSAGE\_SEVERITY\_UNSPECIFIED | 0 | none |
| INGEST\_MESSAGE\_SEVERITY\_INFO | 1 | Information, for example where to view the run. |
| INGEST\_MESSAGE\_SEVERITY\_WARNING | 2 | Something the user should know about the stored run, for example that part of what was reported was not stored. |
### ReconciliationRunFailureCategory
(synq.agent.recon.v1.ReconciliationRunFailureCategory)
ReconciliationRunFailureCategory classifies why a run ended in FAILED.
Clients use this to render appropriate user-facing messages.
| Name | Number | Description |
| - | - | - |
| RECONCILIATION\_RUN\_FAILURE\_CATEGORY\_UNSPECIFIED | 0 | none |
| RECONCILIATION\_RUN\_FAILURE\_CATEGORY\_CONNECTIVITY | 1 | Could not connect to source/target warehouse or other upstream. |
| RECONCILIATION\_RUN\_FAILURE\_CATEGORY\_CONFIG\_INVALID | 2 | Suite config or environment was rejected at execution time. |
| RECONCILIATION\_RUN\_FAILURE\_CATEGORY\_EXECUTION\_ERROR | 3 | Reconciliation execution itself returned an error. |
| RECONCILIATION\_RUN\_FAILURE\_CATEGORY\_INTERNAL | 4 | Unexpected internal error. |
| RECONCILIATION\_RUN\_FAILURE\_CATEGORY\_TIMEOUT | 5 | Run exceeded its configured wall-clock execution timeout and was cancelled before completing. |
### ReconciliationRunStatus
(synq.agent.recon.v1.ReconciliationRunStatus)
ReconciliationRunStatus is the lifecycle state of a single reconciliation run.
| Name | Number | Description |
| - | - | - |
| RECONCILIATION\_RUN\_STATUS\_UNSPECIFIED | 0 | none |
| RECONCILIATION\_RUN\_STATUS\_SCHEDULED | 1 | Run has been queued but not yet picked up by the worker. |
| RECONCILIATION\_RUN\_STATUS\_IN\_PROGRESS | 2 | Worker has started executing the run. |
| RECONCILIATION\_RUN\_STATUS\_SUCCEEDED | 3 | Run completed successfully. |
| RECONCILIATION\_RUN\_STATUS\_FAILED | 4 | Run completed with a failure. |
| RECONCILIATION\_RUN\_STATUS\_CANCELLED | 5 | Run was skipped or cancelled before/during execution (e.g. suite deleted, deployment paused). |
### ReconciliationRunTrigger
(synq.agent.recon.v1.ReconciliationRunTrigger)
ReconciliationRunTrigger describes why a run was created.
| Name | Number | Description |
| - | - | - |
| RECONCILIATION\_RUN\_TRIGGER\_UNSPECIFIED | 0 | none |
| RECONCILIATION\_RUN\_TRIGGER\_ADHOC | 1 | User submitted an ad-hoc suite via SubmitAdhocSuite. |
| RECONCILIATION\_RUN\_TRIGGER\_SCHEDULED | 2 | Scheduler enqueued the run from a deployment / managed-suite cron. |
| RECONCILIATION\_RUN\_TRIGGER\_API | 3 | External API call triggered the run (e.g. CI hook, future TriggerDeployment RPC). |
### SuiteValidationSeverity
(synq.agent.recon.v1.SuiteValidationSeverity)
The severity of a SuiteValidationIssue.
| Name | Number | Description |
| - | - | - |
| SUITE\_VALIDATION\_SEVERITY\_UNSPECIFIED | 0 | none |
| SUITE\_VALIDATION\_SEVERITY\_ERROR | 1 | The suite has a problem that you must correct before you save or run it. |
| SUITE\_VALIDATION\_SEVERITY\_WARNING | 2 | The suite can run, but the result is possibly not what you want. Examples are a query that reads the current time, a window that filters no rows, and a duration that is too long to be correct. You can save a suite that has warnings. |
### DeploymentAction
(synq.agent.recon.v1.DeploymentAction)
| Name | Number | Description |
| - | - | - |
| DEPLOYMENT\_ACTION\_UNSPECIFIED | 0 | none |
| DEPLOYMENT\_ACTION\_PROMOTED | 1 | The suite reached production for the first time, under a new deployment id. Only PROMOTED and REPROMOTED replace the frozen suite snapshot; every other action carries the previous snapshot forward unchanged. |
| DEPLOYMENT\_ACTION\_REPROMOTED | 2 | A suite already in production was published again, keeping its deployment id and replacing the frozen snapshot with the current configuration. |
| DEPLOYMENT\_ACTION\_SCHEDULE\_CHANGED | 3 | The run schedule changed. The suite configuration itself is untouched. |
| DEPLOYMENT\_ACTION\_MAPPINGS\_CHANGED | 4 | The bindings from the suite's connection names to workspace integrations changed. |
| DEPLOYMENT\_ACTION\_PAUSED | 5 | Scheduled runs were suspended. The deployment stays in production. |
| DEPLOYMENT\_ACTION\_RESUMED | 6 | Scheduled runs were re-enabled after a pause. |
| DEPLOYMENT\_ACTION\_UNPROMOTED | 7 | The suite was withdrawn from production. Distinct from PAUSED: this retires the deployment rather than suspending its schedule. |
| DEPLOYMENT\_ACTION\_ANNOTATIONS\_CHANGED | 8 | The annotations attached to the deployment changed. |
| DEPLOYMENT\_ACTION\_FAILURE\_IMPACTS\_CHANGED | 9 | The per-reconciliation failure impact settings changed — which tables a failing reconciliation raises an issue on. |
| DEPLOYMENT\_ACTION\_CATEGORIES\_CHANGED | 10 | The categories declared for the deployment's reconciliation checks changed. |
### UnresolvedConnectionReason
(synq.agent.recon.v1.UnresolvedConnectionReason)
Reason why a single connection could not be resolved.
| Name | Number | Description |
| - | - | - |
| UNRESOLVED\_CONNECTION\_REASON\_UNSPECIFIED | 0 | none |
| UNRESOLVED\_CONNECTION\_REASON\_NOT\_PROVIDED | 1 | No mapping was supplied and no workspace integration matches the connection name. |
| UNRESOLVED\_CONNECTION\_REASON\_INTEGRATION\_NOT\_FOUND | 2 | Mapping references an integration that does not exist or the caller cannot see it. |
| UNRESOLVED\_CONNECTION\_REASON\_CREDENTIAL\_NOT\_ALLOWED | 3 | Mapping uses a credential plane the caller is not allowed to use (e.g. user\_integration\_id without a user identity, or workspace integration\_id without SCOPE\_INTEGRATION\_EDIT). |
| UNRESOLVED\_CONNECTION\_REASON\_CAPABILITY\_DISABLED | 4 | Integration exists but the allow\_reconciliation\_adhoc / allow\_reconciliation\_deploy capability gate is disabled for it. |
| UNRESOLVED\_CONNECTION\_REASON\_PLATFORM\_MISMATCH | 5 | The connection is named after a workspace integration, and the mapping binds it to an integration on a different platform (a Snowflake connection mapped to a Postgres integration). The suite's SQL for that side is written for the platform it names. |
| UNRESOLVED\_CONNECTION\_REASON\_DISABLED\_FOR\_WORKSPACE | 6 | Reconciliation (ad-hoc or deployed, whichever was asked for) is turned off for the whole workspace, for every integration and every credential. No mapping resolves it. |
### AggregateDivergenceType
(synq.agent.recon.v1.AggregateDivergenceType)
AggregateDivergenceType categorizes how an aggregate group diverges.
| Name | Number | Description |
| - | - | - |
| AGGREGATE\_DIVERGENCE\_TYPE\_UNSPECIFIED | 0 | none |
| AGGREGATE\_DIVERGENCE\_TYPE\_SUBGROUP\_MISMATCH | 1 | The group is present on both sides and its measures diverge, at a level that still has a grouping level beneath it. Where drill\_down is populated the divergence is accounted for by the descendants, so this node is a path to the mismatches rather than a mismatch in its own right. Only when drill\_down is absent does it stand as the mismatch itself. |
| AGGREGATE\_DIVERGENCE\_TYPE\_MISSING\_IN\_SOURCE | 2 | Group exists in target but not in source. |
| AGGREGATE\_DIVERGENCE\_TYPE\_MISSING\_IN\_TARGET | 3 | Group exists in source but not in target. |
| AGGREGATE\_DIVERGENCE\_TYPE\_MEASURE\_MISMATCH | 4 | The group is present on both sides and its measures diverge at the deepest grouping level, so it is a mismatch in its own right and carries the investigation queries. Not a synonym for MISMATCH: the two differ by whether anything is grouped below, and only this one is counted directly in the divergent-group totals. |
### AuditOutcome
(synq.agent.recon.v1.AuditOutcome)
AuditOutcome describes the overall outcome of a reconciliation run.
Values are ordered by severity: passed \< within\_threshold \< mismatched \< failed.
| Name | Number | Description |
| - | - | - |
| AUDIT\_OUTCOME\_UNSPECIFIED | 0 | none |
| AUDIT\_OUTCOME\_PASSED | 1 | All reconciliations completed and matched exactly. |
| AUDIT\_OUTCOME\_MISMATCHED\_WITHIN\_THRESHOLD | 2 | Differences were found but all were within configured thresholds. No reconciliation had hard mismatches or infrastructure errors. This is a warning-level outcome. |
| AUDIT\_OUTCOME\_MISMATCHED | 3 | At least one reconciliation found data differences exceeding configured thresholds (or no thresholds were configured). |
| AUDIT\_OUTCOME\_FAILED | 4 | At least one reconciliation encountered an infrastructure error (connection failure, query error, setup failure, etc.). |
### DrillStopReason
(synq.agent.recon.v1.DrillStopReason)
DrillStopReason explains why bisection stopped at a particular node.
| Name | Number | Description |
| - | - | - |
| DRILL\_STOP\_REASON\_UNSPECIFIED | 0 | none |
| DRILL\_STOP\_REASON\_THRESHOLD\_REACHED | 1 | The segment narrowed to at most the configured number of rows, so it is reported as-is. Drilling further would still narrow it, which is why this and MAX\_DEPTH\_REACHED are the two reasons a later run can resume from. |
| DRILL\_STOP\_REASON\_MAX\_DEPTH\_REACHED | 2 | The configured depth limit was hit before the segment got small enough. Resumable: raising the limit and drilling again narrows it further. |
| DRILL\_STOP\_REASON\_EMPTY\_SEGMENT | 3 | Neither side had any rows in the segment, so there was nothing to compare. |
| DRILL\_STOP\_REASON\_CANNOT\_SPLIT | 4 | The key range cannot be subdivided any further — every remaining row shares one key value. Unlike the two resumable reasons above, drilling again will not narrow this segment. |
| DRILL\_STOP\_REASON\_MATCHED | 5 | Source and target agreed, so the segment needed no further drilling. |
| DRILL\_STOP\_REASON\_ERROR | 6 | A query failed while comparing the segment; its result is unknown rather than matched or mismatched. The failure is on the run's query records. |
### MeasureVerdictReason
(synq.agent.recon.v1.MeasureVerdictReason)
MeasureVerdictReason explains the threshold evaluation outcome.
MeasureVerdictReason explains why a measure was or was not treated as
diverging. Thresholds are tried in order — absolute first, then percentage —
and the first one the difference falls inside decides the verdict.
| Name | Number | Description |
| - | - | - |
| MEASURE\_VERDICT\_REASON\_UNSPECIFIED | 0 | none |
| MEASURE\_VERDICT\_REASON\_EXACT\_MATCH | 1 | Source and target agreed exactly, so no threshold was consulted. |
| MEASURE\_VERDICT\_REASON\_WITHIN\_ABSOLUTE\_THRESHOLD | 2 | The values differ, but by no more than the configured absolute threshold. Not treated as divergent. |
| MEASURE\_VERDICT\_REASON\_WITHIN\_PERCENTAGE\_THRESHOLD | 3 | The values differ by more than the absolute threshold allowed (or none was set), but fall inside the configured percentage threshold. Not treated as divergent. The percentage is computed against whichever side the PercentageBasis selects. |
| MEASURE\_VERDICT\_REASON\_EXCEEDED\_ALL\_THRESHOLDS | 4 | The values differ by more than every configured threshold. Divergent. |
| MEASURE\_VERDICT\_REASON\_NO\_THRESHOLDS\_CONFIGURED | 5 | The values differ and no threshold was configured to tolerate it, so any difference counts. Divergent — distinct from EXCEEDED\_ALL\_THRESHOLDS, where thresholds existed and were exceeded. |
### QueryRole
(synq.agent.recon.v1.QueryRole)
QueryRole identifies whether a query targets the source or target database.
| Name | Number | Description |
| - | - | - |
| QUERY\_ROLE\_UNSPECIFIED | 0 | none |
| QUERY\_ROLE\_SOURCE | 1 | none |
| QUERY\_ROLE\_TARGET | 2 | none |
### ReconciliationOutcome
(synq.agent.recon.v1.ReconciliationOutcome)
ReconciliationOutcome describes the outcome of a single reconciliation.
Distinguishes infrastructure errors from data comparison results.
Values are ordered by severity: passed \< within\_threshold \< mismatched \< failed.
| Name | Number | Description |
| - | - | - |
| RECONCILIATION\_OUTCOME\_UNSPECIFIED | 0 | none |
| RECONCILIATION\_OUTCOME\_PASSED | 1 | Reconciliation completed and data matched exactly — no differences found. |
| RECONCILIATION\_OUTCOME\_MISMATCHED\_WITHIN\_THRESHOLD | 2 | Reconciliation found data differences but all differences were within configured thresholds. This is a warning-level outcome — data differs but within acceptable tolerance. |
| RECONCILIATION\_OUTCOME\_MISMATCHED | 3 | Reconciliation completed but found data differences that exceed configured thresholds (or no thresholds were configured). |
| RECONCILIATION\_OUTCOME\_FAILED | 4 | Reconciliation encountered an infrastructure error (connection failure, query error, setup failure, etc.) and could not complete the comparison. |
### RowMismatchType
(synq.agent.recon.v1.RowMismatchType)
RowMismatchType categorizes a single row-level mismatch.
The source/target sense of these values matches SegmentMismatchType and
AggregateDivergenceType: source is numbered before target everywhere.
| Name | Number | Description |
| - | - | - |
| ROW\_MISMATCH\_TYPE\_UNSPECIFIED | 0 | none |
| ROW\_MISMATCH\_TYPE\_MISSING\_IN\_SOURCE | 1 | The row is present in the target but absent from the source. |
| ROW\_MISMATCH\_TYPE\_MISSING\_IN\_TARGET | 2 | The row is present in the source but absent from the target. |
| ROW\_MISMATCH\_TYPE\_MODIFIED | 3 | The row exists on both sides but its contents differ. |
### SegmentMismatchType
(synq.agent.recon.v1.SegmentMismatchType)
SegmentMismatchType categorizes how rows differ between source and target.
A mismatch leaf carries every type that applies, so a segment whose two
sides differ in both row count and content reports COUNT\_MISMATCH and
DATA\_MISMATCH together.
| Name | Number | Description |
| - | - | - |
| SEGMENT\_MISMATCH\_TYPE\_UNSPECIFIED | 0 | none |
| SEGMENT\_MISMATCH\_TYPE\_MISSING\_IN\_SOURCE | 1 | The segment holds rows on the target side but none on the source side. |
| SEGMENT\_MISMATCH\_TYPE\_MISSING\_IN\_TARGET | 2 | The segment holds rows on the source side but none on the target side. |
| SEGMENT\_MISMATCH\_TYPE\_COUNT\_MISMATCH | 3 | Both sides hold rows, but not the same number of them. |
| SEGMENT\_MISMATCH\_TYPE\_DATA\_MISMATCH | 4 | Both sides hold rows and their checksums disagree, so at least one row differs in content. Reported independently of the row counts. |
| SEGMENT\_MISMATCH\_TYPE\_UNKNOWN | 5 | The segment was reported as mismatched but none of the categories above applied. Distinct from UNSPECIFIED, which means the field was never set. |
### StageType
(synq.agent.recon.v1.StageType)
StageType identifies the type of reconciliation stage.
| Name | Number | Description |
| - | - | - |
| STAGE\_TYPE\_UNSPECIFIED | 0 | none |
| STAGE\_TYPE\_QUICK\_CHECK | 1 | Single-query count + checksum comparison. |
| STAGE\_TYPE\_BISECTION\_DRILL | 2 | Recursive bisection drill-down to locate mismatches. |
| STAGE\_TYPE\_AGGREGATE\_CHECK | 3 | Aggregate GROUP BY comparison with measures and thresholds. |
### HashAlgorithm
(synq.agent.recon.v1.HashAlgorithm)
HashAlgorithm specifies the hash function used for row checksums
in ROW\_CHECKSUM mode. Different databases natively support different
hash functions; choosing one supported by both sides avoids emulation overhead.
| Name | Number | Description |
| - | - | - |
| HASH\_ALGORITHM\_UNSPECIFIED | 0 | Auto-negotiates the best common algorithm between source and target. Prefers the fastest algorithm supported natively by both databases. |
| HASH\_ALGORITHM\_MD5 | 1 | MD5 — widely supported across all databases. Slowest but most compatible. |
| HASH\_ALGORITHM\_FARM\_FINGERPRINT | 2 | FarmHash Fingerprint64 — native to BigQuery. Fast for BigQuery-to-BigQuery. |
| HASH\_ALGORITHM\_XXHASH64 | 3 | xxHash64 — native to Databricks and Snowflake. Fast for those dialects. |
### MismatchDetailLevel
(synq.agent.recon.v1.MismatchDetailLevel)
MismatchDetailLevel controls how much detail is included in results for mismatched rows.
Levels are ordered by increasing data exposure.
| Name | Number | Description |
| - | - | - |
| MISMATCH\_DETAIL\_LEVEL\_UNSPECIFIED | 0 | Default: COUNT\_ONLY. |
| MISMATCH\_DETAIL\_LEVEL\_COUNT\_ONLY | 1 | Only report mismatch counts per segment. No row-level information is exposed. Privacy-safe. |
| MISMATCH\_DETAIL\_LEVEL\_WITH\_KEYS | 2 | Include primary key values of mismatched rows. Reveals which rows differ, but not the actual data values. |
| MISMATCH\_DETAIL\_LEVEL\_DETAILED | 3 | Include full column values for mismatched rows. Requires consent\_acknowledged = true. Warning: may expose sensitive data in the audit log. |
### ReconciliationFailureImpact
(synq.agent.recon.v1.ReconciliationFailureImpact)
ReconciliationFailureImpact controls whether a reconciliation case's referenced
tables (resolved from the source/target SQL at deploy time) inherit the
case's failure status, or are only attached to the run for visibility.
This is a deployment-time policy, not part of the suite YAML — it is
set on PromoteSuite / UpdateDeploymentCaseImpacts and stored on the
promoted deployment, not on the authored config. Letting operators
flip impact without editing the suite keeps configuration declarative
and lets the FE offer "apply to all" without round-tripping YAML.
| Name | Number | Description |
| - | - | - |
| RECONCILIATION\_FAILURE\_IMPACT\_UNSPECIFIED | 0 | Defaults to RAISE\_ISSUE\_ON\_TABLES. |
| RECONCILIATION\_FAILURE\_IMPACT\_RAISE\_ISSUE\_ON\_TABLES | 1 | The referenced tables are treated as outcomes of the run: a failed case opens issues on each table, and the table's overall health reflects the reconciliation result. Use this when the reconciliation authoritatively validates the tables it references. |
| RECONCILIATION\_FAILURE\_IMPACT\_SHOW\_ON\_TABLES | 2 | The referenced tables appear on the run for visibility only. The run is listed in each table's run history but never raises an issue on them. Use this for advisory checks that should not impact the table's published health. |
### ReconciliationMode
(synq.agent.recon.v1.ReconciliationMode)
ReconciliationMode defines the comparison strategy for a reconciliation.
Modes are ordered by depth of comparison: ROW\_COUNT \< ROW\_CHECKSUM \< AGGREGATE.
| Name | Number | Description |
| - | - | - |
| RECONCILIATION\_MODE\_UNSPECIFIED | 0 | none |
| RECONCILIATION\_MODE\_ROW\_COUNT | 1 | Compare row counts only — no data access, just verifies both sides have the same number of rows. Fastest mode, useful as a smoke test. |
| RECONCILIATION\_MODE\_ROW\_CHECKSUM | 2 | Row-level checksum comparison with hierarchical bisection drill-down to locate individual mismatched rows. The default and most thorough mode for exact row-by-row comparison. |
| RECONCILIATION\_MODE\_AGGREGATE | 3 | Aggregate comparison — GROUP BY with configurable measures (SUM, AVG, etc.) and tolerance thresholds. Best for large datasets where exact row matching is impractical, or when business-level metrics matter more than individual rows. |
### SegmentationStrategy
(synq.agent.recon.v1.SegmentationStrategy)
SegmentationStrategy defines how key ranges are split during bisection.
The strategy affects both performance and privacy characteristics.
| Name | Number | Description |
| - | - | - |
| SEGMENTATION\_STRATEGY\_UNSPECIFIED | 0 | Auto: defaults to quantile. |
| SEGMENTATION\_STRATEGY\_QUANTILE | 1 | NTILE-based key-range splitting. Uses the key column's actual values to create equal-count segments. Note: exposes key values in segment boundaries in the audit log. |
| SEGMENTATION\_STRATEGY\_HASH | 2 | Hash-based bucketing followed by range drill-down. Uses a hash of the key column for segmentation. Privacy-safe: does not expose raw key values in the audit log. |
| SEGMENTATION\_STRATEGY\_TIME | 3 | Time-based partitioning followed by range drill-down. Splits by time buckets using the configured time\_column and time\_granularity. Requires time\_column to be set. Best for time-series data with a natural temporal ordering (e.g., event logs, daily snapshots). |
### TimeGranularity
(synq.agent.recon.v1.TimeGranularity)
TimeGranularity defines the granularity for time-based segmentation.
| Name | Number | Description |
| - | - | - |
| TIME\_GRANULARITY\_UNSPECIFIED | 0 | Default: DAY. |
| TIME\_GRANULARITY\_HOUR | 1 | none |
| TIME\_GRANULARITY\_DAY | 2 | none |
| TIME\_GRANULARITY\_WEEK | 3 | none |
| TIME\_GRANULARITY\_MONTH | 4 | none |
| TIME\_GRANULARITY\_QUARTER | 5 | none |
| TIME\_GRANULARITY\_YEAR | 6 | none |
### TimestampPrecision
(synq.agent.recon.v1.TimestampPrecision)
TimestampPrecision is how much of a second a timestamp column is compared
to. Both sides are cut to it, never rounded, so a value that differs only
beyond it matches.
| Name | Number | Description |
| - | - | - |
| TIMESTAMP\_PRECISION\_UNSPECIFIED | 0 | none |
| TIMESTAMP\_PRECISION\_SECOND | 1 | Whole seconds. |
| TIMESTAMP\_PRECISION\_MILLISECOND | 2 | Milliseconds, three fractional digits. |
| TIMESTAMP\_PRECISION\_MICROSECOND | 3 | Microseconds, six fractional digits. |
| TIMESTAMP\_PRECISION\_EXACT | 4 | Every fractional digit both sides hold, without cutting either: on one kind of database the column is compared as it is held, and between two the sides must hold the same precision, at most microseconds, otherwise the reconciliation fails rather than cutting the finer side. |
### WindowStrategy
(synq.agent.recon.v1.WindowStrategy)
WindowStrategy defines how the time window boundaries are computed.
| Name | Number | Description |
| - | - | - |
| WINDOW\_STRATEGY\_UNSPECIFIED | 0 | Default: SLIDING. |
| WINDOW\_STRATEGY\_SLIDING | 1 | Sliding window: window\_start = now - lookback. Each run covers a different absolute time range. |
| WINDOW\_STRATEGY\_FIXED | 2 | Fixed window: boundaries are aligned to calendar periods. Each run within the same period covers the same absolute time range. |
### RelationshipWriteOutcome
(synq.entities.custom.v1.RelationshipWriteOutcome)
RelationshipWriteOutcome is the effect one requested write had.
| Name | Number | Description |
| - | - | - |
| RELATIONSHIP\_WRITE\_OUTCOME\_UNSPECIFIED | 0 | none |
| RELATIONSHIP\_WRITE\_OUTCOME\_CREATED | 1 | Nothing joined these two entities; the relationship was created. |
| RELATIONSHIP\_WRITE\_OUTCOME\_UPDATED | 2 | The two entities were already joined; the relationship was replaced. |
| RELATIONSHIP\_WRITE\_OUTCOME\_DELETED | 3 | The relationship existed and has been removed. |
| RELATIONSHIP\_WRITE\_OUTCOME\_NOT\_FOUND | 4 | Nothing joined these two entities, so the delete removed nothing. |
### ExecutionStatus
(synq.entities.custom.v1.ExecutionStatus)
| Name | Number | Description |
| - | - | - |
| EXECUTION\_STATUS\_UNSPECIFIED | 0 | none |
| EXECUTION\_STATUS\_OK | 1 | none |
| EXECUTION\_STATUS\_WARN | 2 | none |
| EXECUTION\_STATUS\_ERROR | 3 | none |
| EXECUTION\_STATUS\_CRITICAL | 4 | none |
### FolderRole
(synq.entities.folders.v1.FolderRole)
FolderRole describes how a folder placement participates in browsing. It
mirrors the placement roles carried by the catalog.
| Name | Number | Description |
| - | - | - |
| FOLDER\_ROLE\_UNSPECIFIED | 0 | Unknown / not reported. Treat as VISIBLE. |
| FOLDER\_ROLE\_PRIMARY | 1 | Browsable and the default display location (breadcrumb) for its members. |
| FOLDER\_ROLE\_VISIBLE | 2 | Browsable, but not the default display location. |
| FOLDER\_ROLE\_HIDDEN | 3 | Match-only: folder filters still match entities through this placement, but it is not shown when browsing. Kept for legacy hierarchies. |
### IssueStatus
(synq.entities.status.v1.IssueStatus)
| Name | Number | Description |
| - | - | - |
| ISSUE\_STATUS\_UNSPECIFIED | 0 | none |
| ISSUE\_STATUS\_OK | 10 | none |
| ISSUE\_STATUS\_WARN | 20 | none |
| ISSUE\_STATUS\_ERROR | 30 | none |
| ISSUE\_STATUS\_CRITICAL | 40 | none |
### NumericCategory
(synq.entities.v1.NumericCategory)
NumericCategory is the kind of number a column holds, which decides how many
significant digits its values carry. It is read from the column's type as the
data warehouse declares it, with the same name meaning a different size on
different warehouses taken into account: a bare FLOAT is single precision on
MySQL and a double on Snowflake.
| Name | Number | Description |
| - | - | - |
| NUMERIC\_CATEGORY\_UNSPECIFIED | 0 | The column does not hold a number, or its type is not recognised. |
| NUMERIC\_CATEGORY\_INTEGER | 1 | A whole number: an integer type, or an exact decimal declared with scale 0 (a Snowflake NUMBER(38,0)). |
| NUMERIC\_CATEGORY\_DECIMAL | 2 | An exact decimal (NUMERIC, DECIMAL, NUMBER) that may hold fractions, sized by its precision and scale when they are known. |
| NUMERIC\_CATEGORY\_FLOAT | 3 | A double-precision binary float, about 15 significant digits. |
| NUMERIC\_CATEGORY\_SINGLE\_PRECISION\_FLOAT | 4 | A single-precision binary float (REAL, FLOAT4, Float32, BINARY\_FLOAT), about 7 significant digits. |
### CodeType
(synq.entities.v1.CodeType)
CodeType describes how to read the body of a Code — which language or
format its `content` is written in, so a client can highlight, parse or
hand it to the right tool.
A producer that cannot tell leaves CODE\_TYPE\_UNSPECIFIED rather than
guessing; treat that as plain text.
| Name | Number | Description |
| - | - | - |
| CODE\_TYPE\_UNSPECIFIED | 0 | none |
| CODE\_TYPE\_SQL | 1 | SQL code (e.g., SELECT, INSERT, UPDATE statements) |
| CODE\_TYPE\_PYTHON | 2 | Python code (e.g., scripts, functions, classes) |
| CODE\_TYPE\_JSON | 3 | JSON data or configuration files |
| CODE\_TYPE\_YAML | 4 | YAML configuration or data files |
| CODE\_TYPE\_DBT | 5 | dbt (data build tool) models and configurations |
| CODE\_TYPE\_SQLMESH | 6 | SQLMesh models and configurations. In most cases this will be similar to SQL code. |
| CODE\_TYPE\_LOOKML | 7 | LookML (Looker Modeling Language) files |
| CODE\_TYPE\_JAVASCRIPT | 8 | JavaScript, as used for the body of a Snowflake stored procedure or user-defined function declared LANGUAGE JAVASCRIPT. |
| CODE\_TYPE\_BASH | 9 | Shell script, as used by an orchestrator task that shells out instead of running SQL or Python. |
| CODE\_TYPE\_DAX | 10 | DAX, the expression language behind Power BI measures and calculated columns. |
| CODE\_TYPE\_JAVA | 11 | Java source, as used for the body of a Snowflake routine declared LANGUAGE JAVA. |
| CODE\_TYPE\_SCALA | 12 | Scala source, as used for the body of a Snowflake routine declared LANGUAGE SCALA. |
| CODE\_TYPE\_TEXT | 13 | Plain text with no known syntax. Distinct from CODE\_TYPE\_UNSPECIFIED: this is a producer stating the body has no language, rather than a producer that could not tell. |
| CODE\_TYPE\_MARKDOWN | 14 | Markdown, as used by a notebook cell or a documentation block that ships beside a model. |
### CodeSource
(synq.entities.v1.CodeSource)
CodeSource names one of the places an entity's code is read from. An entity
usually has code in more than one of them — a dbt model has its own source
and the warehouse table's DDL — and each is read independently, so one
being unavailable does not hide the others.
| Name | Number | Description |
| - | - | - |
| CODE\_SOURCE\_UNSPECIFIED | 0 | none |
| CODE\_SOURCE\_SQL\_DEFINITION | 1 | The compiled SQL definition of a table or view. |
| CODE\_SOURCE\_ENTITY\_DEFINITION | 2 | Code carried by the entity itself: a transformation model's source, a stored routine's body, an orchestration task's script. |
| CODE\_SOURCE\_REPOSITORY\_FILE | 3 | A file read from a connected repository. |
| CODE\_SOURCE\_CUSTOM\_ENTITY | 4 | Code attached to a custom entity. |
| CODE\_SOURCE\_SQL\_TEST | 5 | The expression of a SQL test. |
| CODE\_SOURCE\_RECONCILIATION | 6 | The configuration of a deployed reconciliation suite or case. |
| CODE\_SOURCE\_MONITOR | 7 | The query a monitor runs against the warehouse. |
### SqlDialect
(synq.entities.v1.SqlDialect)
| Name | Number | Description |
| - | - | - |
| SQL\_DIALECT\_UNSPECIFIED | 0 | none |
| SQL\_DIALECT\_BIGQUERY | 1 | none |
| SQL\_DIALECT\_CLICKHOUSE | 2 | none |
| SQL\_DIALECT\_DATABRICKS | 3 | none |
| SQL\_DIALECT\_MYSQL | 4 | none |
| SQL\_DIALECT\_POSTGRESQL | 5 | none |
| SQL\_DIALECT\_REDSHIFT | 6 | none |
| SQL\_DIALECT\_SNOWFLAKE | 7 | none |
| SQL\_DIALECT\_DUCKDB | 8 | none |
| SQL\_DIALECT\_TRINO | 9 | none |
| SQL\_DIALECT\_MSSQL | 10 | none |
| SQL\_DIALECT\_ORACLE | 11 | none |
| SQL\_DIALECT\_ATHENA | 12 | none |
| SQL\_DIALECT\_FABRIC | 13 | none |
| SQL\_DIALECT\_DB2 | 14 | none |
### EntityType
(synq.entities.v1.EntityType)
| Name | Number | Description |
| - | - | - |
| ENTITY\_TYPE\_UNSPECIFIED | 0 | none |
| ENTITY\_TYPE\_BQ\_TABLE | 103 | none |
| ENTITY\_TYPE\_BQ\_VIEW | 105 | none |
| ENTITY\_TYPE\_LOOKER\_LOOK | 201 | none |
| ENTITY\_TYPE\_LOOKER\_EXPLORE | 203 | none |
| ENTITY\_TYPE\_LOOKER\_VIEW | 207 | none |
| ENTITY\_TYPE\_LOOKER\_DASHBOARD | 208 | none |
| ENTITY\_TYPE\_LOOKER\_INSTANCE | 210 | none |
| ENTITY\_TYPE\_DBT\_MODEL | 301 | none |
| ENTITY\_TYPE\_DBT\_TEST | 302 | none |
| ENTITY\_TYPE\_DBT\_SOURCE | 303 | none |
| ENTITY\_TYPE\_DBT\_PROJECT | 306 | none |
| ENTITY\_TYPE\_DBT\_METRIC | 307 | none |
| ENTITY\_TYPE\_DBT\_SNAPSHOT | 310 | none |
| ENTITY\_TYPE\_DBT\_SEED | 311 | none |
| ENTITY\_TYPE\_DBT\_ANALYSIS | 312 | none |
| ENTITY\_TYPE\_DBT\_EXPOSURE | 313 | none |
| ENTITY\_TYPE\_DBT\_GROUP | 314 | none |
| ENTITY\_TYPE\_DBT\_SEMANTIC\_MODEL | 315 | none |
| ENTITY\_TYPE\_DBT\_CLOUD\_PROJECT | 352 | none |
| ENTITY\_TYPE\_DBT\_CLOUD\_JOB | 353 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_TABLE | 503 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_VIEW | 508 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_STREAM | 509 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_DYNAMIC\_TABLE | 510 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_TASK | 511 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_EXTERNAL\_TABLE | 512 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_MATERIALIZED\_VIEW | 513 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_PROCEDURE | 514 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_FUNCTION | 515 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_SEMANTIC\_VIEW | 516 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_STAGE | 517 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_STREAMLIT | 518 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_NOTEBOOK | 519 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_WAREHOUSE | 520 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_PIPE | 521 | none |
| ENTITY\_TYPE\_SNOWFLAKE\_CORTEX\_SEARCH\_SERVICE | 522 | none |
| ENTITY\_TYPE\_REDSHIFT\_TABLE | 803 | none |
| ENTITY\_TYPE\_REDSHIFT\_VIEW | 805 | none |
| ENTITY\_TYPE\_REDSHIFT\_PROCEDURE | 806 | none |
| ENTITY\_TYPE\_REDSHIFT\_FUNCTION | 807 | none |
| ENTITY\_TYPE\_TABLEAU\_EMBEDDED | 1101 | none |
| ENTITY\_TYPE\_TABLEAU\_PUBLISHED | 1102 | none |
| ENTITY\_TYPE\_TABLEAU\_CUSTOM\_SQL | 1103 | none |
| ENTITY\_TYPE\_TABLEAU\_TABLE | 1104 | none |
| ENTITY\_TYPE\_TABLEAU\_SHEET | 1105 | none |
| ENTITY\_TYPE\_TABLEAU\_DASHBOARD | 1106 | none |
| ENTITY\_TYPE\_TABLEAU\_WORKBOOK | 1107 | none |
| ENTITY\_TYPE\_TABLEAU\_SITE | 1108 | none |
| ENTITY\_TYPE\_AIRFLOW\_DAG | 1201 | none |
| ENTITY\_TYPE\_AIRFLOW\_TASK | 1202 | none |
| ENTITY\_TYPE\_CLICKHOUSE\_TABLE | 1303 | none |
| ENTITY\_TYPE\_CLICKHOUSE\_VIEW | 1305 | none |
| ENTITY\_TYPE\_ANOMALY\_MONITOR | 1403 | none |
| ENTITY\_TYPE\_ANOMALY\_MONITOR\_SEGMENT | 1404 | none |
| ENTITY\_TYPE\_SQLTEST\_TEST | 1421 | none |
| ENTITY\_TYPE\_RECON\_SUITE | 1431 | none |
| ENTITY\_TYPE\_RECON\_CASE | 1432 | none |
| ENTITY\_TYPE\_LINEAGE\_FOLD | 1504 | none |
| ENTITY\_TYPE\_POSTGRES\_TABLE | 1603 | none |
| ENTITY\_TYPE\_POSTGRES\_VIEW | 1605 | none |
| ENTITY\_TYPE\_MYSQL\_TABLE | 1703 | none |
| ENTITY\_TYPE\_MYSQL\_VIEW | 1705 | none |
| ENTITY\_TYPE\_DATABRICKS\_WAREHOUSE | 1801 | none |
| ENTITY\_TYPE\_DATABRICKS\_TABLE | 1804 | none |
| ENTITY\_TYPE\_DATABRICKS\_VIEW | 1805 | none |
| ENTITY\_TYPE\_DATABRICKS\_JOB | 1807 | none |
| ENTITY\_TYPE\_DATABRICKS\_JOB\_TASK | 1808 | none |
| ENTITY\_TYPE\_DATABRICKS\_NOTEBOOK | 1809 | none |
| ENTITY\_TYPE\_DATABRICKS\_QUERY | 1810 | none |
| ENTITY\_TYPE\_DATABRICKS\_DASHBOARD | 1811 | none |
| ENTITY\_TYPE\_DATABRICKS\_METRIC\_VIEW | 1812 | none |
| ENTITY\_TYPE\_SQLMESH\_PROJECT | 1901 | none |
| ENTITY\_TYPE\_SQLMESH\_SQL\_MODEL | 1902 | none |
| ENTITY\_TYPE\_SQLMESH\_PYTHON\_MODEL | 1903 | none |
| ENTITY\_TYPE\_SQLMESH\_EXTERNAL | 1904 | none |
| ENTITY\_TYPE\_SQLMESH\_SEED | 1905 | none |
| ENTITY\_TYPE\_SQLMESH\_AUDIT | 1906 | none |
| ENTITY\_TYPE\_SQLMESH\_UNIT\_TEST | 1907 | none |
| ENTITY\_TYPE\_SQLMESH\_ENVIRONMENT | 1908 | none |
| ENTITY\_TYPE\_SQLMESH\_SNAPSHOT | 1909 | none |
| ENTITY\_TYPE\_DUCKDB\_TABLE | 2003 | none |
| ENTITY\_TYPE\_DUCKDB\_VIEW | 2005 | none |
| ENTITY\_TYPE\_TRINO\_TABLE | 2104 | none |
| ENTITY\_TYPE\_TRINO\_VIEW | 2105 | none |
| ENTITY\_TYPE\_ATLAN\_ASSET | 2201 | none |
| ENTITY\_TYPE\_ATLAN\_INTEGRATION | 2202 | none |
| ENTITY\_TYPE\_COALESCE\_PROJECT | 2301 | none |
| ENTITY\_TYPE\_COALESCE\_ENVIRONMENT | 2302 | none |
| ENTITY\_TYPE\_COALESCE\_NODE | 2303 | none |
| ENTITY\_TYPE\_COALESCE\_NODE\_TEST | 2304 | none |
| ENTITY\_TYPE\_COALESCE\_JOB | 2305 | none |
| ENTITY\_TYPE\_CASTORDOC\_TABLE | 2404 | none |
| ENTITY\_TYPE\_CASTORDOC\_DASHBOARD | 2405 | none |
| ENTITY\_TYPE\_CASTORDOC\_VIEW | 2406 | none |
| ENTITY\_TYPE\_CASTORDOC\_TILE | 2407 | none |
| ENTITY\_TYPE\_CASTORDOC\_VIZ\_MODEL | 2408 | none |
| ENTITY\_TYPE\_POWERBI\_REPORT | 3001 | none |
| ENTITY\_TYPE\_POWERBI\_TILE | 3002 | none |
| ENTITY\_TYPE\_POWERBI\_DATASET | 3003 | none |
| ENTITY\_TYPE\_POWERBI\_DASHBOARD | 3004 | none |
| ENTITY\_TYPE\_POWERBI\_TENANT | 3005 | none |
| ENTITY\_TYPE\_THOUGHTSPOT\_LIVEBOARD | 3101 | none |
| ENTITY\_TYPE\_THOUGHTSPOT\_WORKSHEET | 3102 | none |
| ENTITY\_TYPE\_SIGMA\_WORKBOOK | 3201 | none |
| ENTITY\_TYPE\_SIGMA\_DATA\_MODEL | 3202 | none |
| ENTITY\_TYPE\_SIGMA\_ORGANIZATION | 3203 | none |
| ENTITY\_TYPE\_DOMO\_PAGE | 3301 | none |
| ENTITY\_TYPE\_DOMO\_DATASET | 3302 | none |
| ENTITY\_TYPE\_MSSQL\_TABLE | 3403 | none |
| ENTITY\_TYPE\_MSSQL\_VIEW | 3405 | none |
| ENTITY\_TYPE\_ORACLE\_TABLE | 3503 | none |
| ENTITY\_TYPE\_ORACLE\_VIEW | 3505 | none |
| ENTITY\_TYPE\_ATHENA\_TABLE | 3604 | none |
| ENTITY\_TYPE\_ATHENA\_VIEW | 3605 | none |
| ENTITY\_TYPE\_FABRIC\_TABLE | 3704 | none |
| ENTITY\_TYPE\_FABRIC\_VIEW | 3705 | none |
| ENTITY\_TYPE\_DB2\_TABLE | 3803 | none |
| ENTITY\_TYPE\_DB2\_VIEW | 3805 | none |
| ENTITY\_TYPE\_LOOKER\_STUDIO\_CUSTOMER | 3901 | none |
| ENTITY\_TYPE\_LOOKER\_STUDIO\_REPORT | 3902 | none |
| ENTITY\_TYPE\_LOOKER\_STUDIO\_DATA\_SOURCE | 3903 | none |
| ENTITY\_TYPE\_CUSTOM\_ENTITY\_GENERIC | 50000 | none |
| ENTITY\_TYPE\_CUSTOM\_ENTITY\_CUSTOM\_TYPE\_MIN | 50001 | none |
| ENTITY\_TYPE\_CUSTOM\_ENTITY\_CUSTOM\_TYPE\_MAX | 59999 | none |
### EntityChangeType
(synq.entities.v1.EntityChangeType)
| Name | Number | Description |
| - | - | - |
| ENTITY\_CHANGE\_TYPE\_UNSPECIFIED | 0 | none |
| ENTITY\_CHANGE\_TYPE\_GIT\_COMMIT | 1 | none |
| ENTITY\_CHANGE\_TYPE\_SQL\_DEFINITION\_CHANGE | 2 | none |
| ENTITY\_CHANGE\_TYPE\_SCHEMA\_CHANGE | 3 | none |
### TableConstraintType
(synq.entities.v1.TableConstraintType)
TableConstraintType represents the type of a database table constraint or index.
| Name | Number | Description |
| - | - | - |
| TABLE\_CONSTRAINT\_TYPE\_UNSPECIFIED | 0 | none |
| TABLE\_CONSTRAINT\_TYPE\_PRIMARY\_KEY | 1 | none |
| TABLE\_CONSTRAINT\_TYPE\_FOREIGN\_KEY | 2 | none |
| TABLE\_CONSTRAINT\_TYPE\_UNIQUE | 3 | none |
| TABLE\_CONSTRAINT\_TYPE\_CHECK | 4 | none |
| TABLE\_CONSTRAINT\_TYPE\_INDEX | 5 | none |
| TABLE\_CONSTRAINT\_TYPE\_SORTING\_KEY | 6 | DWH-specific constraint/ordering types |
| TABLE\_CONSTRAINT\_TYPE\_PARTITION\_BY | 7 | none |
| TABLE\_CONSTRAINT\_TYPE\_CLUSTER\_BY | 8 | none |
| TABLE\_CONSTRAINT\_TYPE\_DISTRIBUTION\_KEY | 9 | none |
| TABLE\_CONSTRAINT\_TYPE\_PROJECTION | 10 | none |
### ExecutionType
(synq.entities.v1.ExecutionType)
| Name | Number | Description |
| - | - | - |
| EXECUTION\_TYPE\_UNSPECIFIED | 0 | none |
| EXECUTION\_TYPE\_DELETION | 1 | none |
| EXECUTION\_TYPE\_SCHEMA\_CHANGE | 2 | none |
| EXECUTION\_TYPE\_SQL\_DEFINITION\_CHANGE | 3 | none |
| EXECUTION\_TYPE\_BQ\_QUERY\_JOB | 101 | none |
| EXECUTION\_TYPE\_BQ\_LOAD\_JOB | 102 | none |
| EXECUTION\_TYPE\_BQ\_COPY\_JOB | 103 | none |
| EXECUTION\_TYPE\_BQ\_EXTRACT\_JOB | 104 | none |
| EXECUTION\_TYPE\_BQ\_QUERY\_LOG | 105 | none |
| EXECUTION\_TYPE\_LOOKER\_USAGE\_REPORT | 201 | none |
| EXECUTION\_TYPE\_DBT\_INVOCATION | 301 | none |
| EXECUTION\_TYPE\_DBT\_NODE\_INVOCATION | 302 | none |
| EXECUTION\_TYPE\_DBT\_NODE\_FRESHNESS | 303 | none |
| EXECUTION\_TYPE\_DBT\_CATALOG\_REFRESH | 304 | none |
| EXECUTION\_TYPE\_DBT\_CLOUD\_JOB\_RUN | 351 | none |
| EXECUTION\_TYPE\_DBT\_CLOUD\_JOB\_RUN\_STEP | 352 | none |
| EXECUTION\_TYPE\_DATABOX\_FRESHNESS | 401 | none |
| EXECUTION\_TYPE\_GIT\_COMMIT | 501 | none |
| EXECUTION\_TYPE\_CLICKHOUSE\_QUERY\_LOG | 601 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG | 701 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_SELECT | 702 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_DROP | 703 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE | 704 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE\_TABLE | 705 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_INSERT | 706 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_MERGE | 707 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_UPDATE | 708 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE\_TABLE\_AS\_SELECT | 709 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_DELETE | 710 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_SHOW | 711 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE\_VIEW | 712 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_MULTI\_STATEMENT | 713 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ROLLBACK | 714 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_TRUNCATE\_TABLE | 715 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_COMMIT | 716 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_BEGIN\_TRANSACTION | 717 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_UNKNOWN | 718 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_SET | 719 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_RENAME\_TABLE | 720 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_GRANT | 721 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ALTER\_TABLE\_ADD\_COLUMN | 722 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_USE | 723 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_COPY | 724 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CALL | 725 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ALTER\_TABLE\_DROP\_COLUMN | 726 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ALTER\_TABLE\_MODIFY\_COLUMN | 727 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_RENAME\_COLUMN | 728 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_DROP\_ROLE | 729 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_DESCRIBE\_QUERY | 730 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE\_CONSTRAINT | 731 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_EXPLAIN | 732 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE\_ROLE | 733 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ALTER | 734 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_DESCRIBE | 735 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_PUT\_FILES | 736 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_GET\_FILES | 737 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ALTER\_SESSION | 738 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_LIST\_FILES | 739 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_EXTERNAL\_TABLE\_REFRESH | 740 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_REFRESH\_DYNAMIC\_TABLE\_AT\_REFRESH\_VERSION | 741 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_REMOVE\_FILES | 742 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_EXECUTE\_TASK | 743 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_ALTER\_TABLE | 744 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_EXECUTE\_STREAMLIT | 745 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_CREATE\_TASK | 746 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_UNLOAD | 747 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_QUERY\_LOG\_DROP\_TASK | 748 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_TASK\_EXECUTION | 790 | none |
| EXECUTION\_TYPE\_SNOWFLAKE\_TASK\_GROUP\_EXECUTION | 791 | none |
| EXECUTION\_TYPE\_REDSHIFT\_QUERY\_LOG | 801 | none |
| EXECUTION\_TYPE\_DATAHUB\_DATA\_PROCESS\_INSTANCE | 901 | none |
| EXECUTION\_TYPE\_DATAHUB\_USAGE\_REPORT | 902 | none |
| EXECUTION\_TYPE\_SYNQ\_CUSTOM\_ENTITY\_EXECUTION | 1001 | none |
| EXECUTION\_TYPE\_SYNQ\_CUSTOM\_ENTITY\_LOG | 1002 | none |
| EXECUTION\_TYPE\_SYNQ\_CUSTOM\_ENTITY\_MEASUREMENT | 1003 | none |
| EXECUTION\_TYPE\_TABLEAU\_USAGE\_REPORT | 1101 | none |
| EXECUTION\_TYPE\_TABLEAU\_EXTRACT | 1102 | none |
| EXECUTION\_TYPE\_AIRFLOW\_DAG | 1201 | none |
| EXECUTION\_TYPE\_AIRFLOW\_TASK | 1202 | none |
| EXECUTION\_TYPE\_ANOMALY\_INTEGRATION\_RUN | 1402 | none |
| EXECUTION\_TYPE\_ANOMALY\_MONITOR\_RUN | 1403 | none |
| EXECUTION\_TYPE\_ANOMALY\_MONITOR\_CORRECTION | 1404 | none |
| EXECUTION\_TYPE\_ANOMALY\_MONITOR\_SEGMENT\_RUN | 1405 | none |
| EXECUTION\_TYPE\_ANOMALY\_MONITOR\_SEGMENT\_CORRECTION | 1406 | none |
| EXECUTION\_TYPE\_ANOMALY\_MONITOR\_FETCH\_RUN | 1407 | none |
| EXECUTION\_TYPE\_SQL\_TEST\_RUN | 1421 | none |
| EXECUTION\_TYPE\_DATABRICKS\_QUERY\_LOG | 1501 | none |
| EXECUTION\_TYPE\_DATABRICKS\_JOB\_RUN | 1502 | none |
| EXECUTION\_TYPE\_DATABRICKS\_JOB\_TASK\_RUN | 1503 | none |
| EXECUTION\_TYPE\_SQLMESH\_PROJECT\_EXECUTION | 1601 | none |
| EXECUTION\_TYPE\_SQLMESH\_MODEL\_EVALUATION | 1602 | none |
| EXECUTION\_TYPE\_SQLMESH\_AUDIT\_CHECK | 1603 | none |
| EXECUTION\_TYPE\_TRINO\_QUERY\_LOG | 2101 | none |
| EXECUTION\_TYPE\_COALESCE\_ENVIRONMENT\_RUN | 2301 | none |
| EXECUTION\_TYPE\_COALESCE\_DEPLOY\_RUN | 2302 | none |
| EXECUTION\_TYPE\_COALESCE\_REFRESH\_RUN | 2303 | none |
| EXECUTION\_TYPE\_COALESCE\_NODE\_RUN | 2304 | none |
| EXECUTION\_TYPE\_COALESCE\_SQL\_TEST\_RUN | 2305 | none |
| EXECUTION\_TYPE\_MSSQL\_QUERY\_LOG | 2401 | none |
| EXECUTION\_TYPE\_ORACLE\_QUERY\_LOG | 2501 | none |
| EXECUTION\_TYPE\_RECON\_SUITE\_RUN | 2601 | none |
| EXECUTION\_TYPE\_RECON\_CASE\_RUN | 2602 | none |
| EXECUTION\_TYPE\_RECON\_DEPLOYMENT | 2603 | none |
| EXECUTION\_TYPE\_FABRIC\_QUERY\_LOG | 2701 | none |
### SqlConstruct
(synq.entities.v1.SqlConstruct)
SqlConstruct enumerates the SQL features detected in an entity's query.
Multiple values can be set on a single statement (e.g. a query with both a
CTE and an INNER JOIN carries HAS\_CTE, HAS\_ANY\_JOIN and INNER\_JOIN).
| Name | Number | Description |
| - | - | - |
| SQL\_CONSTRUCT\_UNSPECIFIED | 0 | none |
| SQL\_CONSTRUCT\_HAS\_ANY\_JOIN | 1 | Aggregate flag: the query contains at least one join of any kind. Always emitted alongside the specific JOIN variant for that join. |
| SQL\_CONSTRUCT\_HAS\_ANY\_SET\_OPERATION | 2 | Aggregate flag: the query contains at least one UNION / INTERSECT / EXCEPT at any nesting level. Always emitted alongside the specific SET variant. |
| SQL\_CONSTRUCT\_HAS\_GROUP\_BY | 3 | The query has a GROUP BY (including GROUP BY ALL). |
| SQL\_CONSTRUCT\_HAS\_CTE | 4 | The query has a WITH clause (CTEs, recursive or not). |
| SQL\_CONSTRUCT\_SET\_UNION | 10 | UNION / UNION ALL set operation. Emitted alongside HAS\_ANY\_SET\_OPERATION. |
| SQL\_CONSTRUCT\_SET\_INTERSECT | 11 | INTERSECT set operation. Emitted alongside HAS\_ANY\_SET\_OPERATION. |
| SQL\_CONSTRUCT\_SET\_EXCEPT | 12 | EXCEPT / MINUS set operation. Emitted alongside HAS\_ANY\_SET\_OPERATION. |
| SQL\_CONSTRUCT\_INNER\_JOIN | 20 | INNER JOIN. Also emitted for an unqualified `JOIN`, which defaults to INNER. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_CROSS\_APPLY\_JOIN | 21 | T-SQL `CROSS APPLY`. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_OUTER\_APPLY\_JOIN | 22 | T-SQL `OUTER APPLY`. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_ARRAY\_JOIN | 23 | ClickHouse `ARRAY JOIN` / `LEFT ARRAY JOIN`. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_CROSS\_JOIN | 24 | Explicit CROSS JOIN (cartesian product). Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_LEFT\_SEMI\_JOIN | 25 | LEFT SEMI JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_RIGHT\_SEMI\_JOIN | 26 | RIGHT SEMI JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_LEFT\_ANTI\_JOIN | 27 | LEFT ANTI JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_RIGHT\_ANTI\_JOIN | 28 | RIGHT ANTI JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_LEFT\_OUTER\_JOIN | 29 | LEFT \[OUTER] JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_RIGHT\_OUTER\_JOIN | 30 | RIGHT \[OUTER] JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_FULL\_OUTER\_JOIN | 31 | FULL \[OUTER] JOIN. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_ASOF\_JOIN | 32 | Snowflake / Databricks `ASOF JOIN`. Emitted alongside HAS\_ANY\_JOIN. |
| SQL\_CONSTRUCT\_READS\_SEMANTIC\_VIEW | 33 | Query reads a Snowflake semantic view via the `SEMANTIC_VIEW(...)` table-valued function. One occurrence is recorded per semantic-view reference. The companion CREATE SEMANTIC VIEW DDL is classified separately, not via this construct. |
| SQL\_CONSTRUCT\_HAS\_AGGREGATE\_FUNCTION | 34 | The query invokes at least one aggregate function (SUM, COUNT, AVG, ...) used as a grouping aggregate. A windowed aggregate (`SUM(x) OVER (...)`) does NOT set this flag — it sets HAS\_WINDOW\_FUNCTION instead, because it produces one row per input row rather than collapsing groups. Aggregate names are recognised per-dialect, including user-defined aggregates. |
| SQL\_CONSTRUCT\_HAS\_WINDOW\_FUNCTION | 35 | The query invokes at least one window function — any function call carrying an `OVER (...)` clause, whether the base is an aggregate (`SUM(x) OVER (...)`) or a dedicated window function (`ROW_NUMBER() OVER (...)`). The window-ness is the `OVER` clause, not the base name. |
| SQL\_CONSTRUCT\_UNSUPPORTED\_SQL\_SYNTAX | 100 | Set when the SQL could not be parsed. Lets unparseable entities be filtered without inspecting the underlying parse error. |
| SQL\_CONSTRUCT\_HAS\_JOIN\_PREDICATES | 101 | Set when the parser read at least one column comparison from the SQL: a join key, or a comparison relating two columns without joining them. |
| SQL\_CONSTRUCT\_HAS\_JOIN\_PAIRS | 102 | Set when at least one join key compares two columns that both belong to known entities and are declared in their schemas. |
### ActivityDirection
(synq.entities.activity.v1alpha.ActivityDirection)
ActivityDirection is how a query touched an entity.
| Name | Number | Description |
| - | - | - |
| ACTIVITY\_DIRECTION\_UNSPECIFIED | 0 | none |
| ACTIVITY\_DIRECTION\_READ | 1 | A statement that writes nothing read the entity. |
| ACTIVITY\_DIRECTION\_INPUT | 2 | A statement that writes another table read the entity, such as a model build. |
| ACTIVITY\_DIRECTION\_WRITE | 3 | Rows landed in the entity. For RUNS, one run of what builds the entity. |
### ActivityLens
(synq.entities.activity.v1alpha.ActivityLens)
ActivityLens picks which directions and which logins count. Coalesce Quality's own queries, a read
of an intermediate table and a login's reads of a table it wrote the same day are left out under
every lens, and writes never depend on it.
| Name | Number | Description |
| - | - | - |
| ACTIVITY\_LENS\_UNSPECIFIED | 0 | Read as CONSUMPTION. |
| ACTIVITY\_LENS\_CONSUMPTION | 1 | Reads by the logins whose reads count toward popularity: people, BI tools, reverse ETL and applications. What BatchGetPopularity counts. |
| ACTIVITY\_LENS\_DEVELOPMENT | 2 | Every INPUT, and the reads of people: what is read to build, and what developers look at. |
| ACTIVITY\_LENS\_MONITORING | 3 | Reads by a service account whose purpose is observability. |
### ActivityMeasure
(synq.entities.activity.v1alpha.ActivityMeasure)
ActivityMeasure is what a count counts. Counts are compared only within one measure.
| Name | Number | Description |
| - | - | - |
| ACTIVITY\_MEASURE\_UNSPECIFIED | 0 | none |
| ACTIVITY\_MEASURE\_QUERIES | 1 | Warehouse queries. |
| ACTIVITY\_MEASURE\_VIEWS | 2 | Opens of a BI asset. |
| ACTIVITY\_MEASURE\_RUNS | 3 | Orchestrated runs of what builds an entity. Always WRITE, with no login. |
### ActivityShape
(synq.entities.activity.v1alpha.ActivityShape)
ActivityShape is how a platform reports a figure.
| Name | Number | Description |
| - | - | - |
| ACTIVITY\_SHAPE\_UNSPECIFIED | 0 | none |
| ACTIVITY\_SHAPE\_INTERVAL | 1 | Occurrences counted per event, hour or day. |
| ACTIVITY\_SHAPE\_CUMULATIVE | 2 | The day's difference between two daily readings of an all-time counter. |
| ACTIVITY\_SHAPE\_TRAILING\_WINDOW | 3 | The platform's own "N in the last M days". |
| ACTIVITY\_SHAPE\_LATEST\_VALUE | 4 | A figure with no stated window. |
### ComputeUnit
(synq.entities.activity.v1alpha.ComputeUnit)
ComputeUnit is what compute\_ms counts on one platform. Each platform reports its own unit, so
compute is never summed across platforms.
| Name | Number | Description |
| - | - | - |
| COMPUTE\_UNIT\_UNSPECIFIED | 0 | The platform reports no compute figure. |
| COMPUTE\_UNIT\_SNOWFLAKE\_XSMALL\_WAREHOUSE\_MS | 1 | Milliseconds of an X-Small warehouse: a query's execution time scaled by its warehouse size and its share of the warehouse. |
| COMPUTE\_UNIT\_BIGQUERY\_SLOT\_MS | 2 | BigQuery slot milliseconds. |
| COMPUTE\_UNIT\_DATABRICKS\_TASK\_MS | 3 | Databricks task milliseconds. |
### PopularityPool
(synq.entities.activity.v1alpha.PopularityPool)
PopularityPool is what a rank compares an entity with.
| Name | Number | Description |
| - | - | - |
| POPULARITY\_POOL\_UNSPECIFIED | 0 | Read as SYSTEM. |
| POPULARITY\_POOL\_SYSTEM | 1 | One measure on one platform instance, such as one Snowflake account. |
| POPULARITY\_POOL\_PLATFORM | 2 | One measure on every instance of one platform. |
| POPULARITY\_POOL\_WORKSPACE | 3 | One measure across the workspace. |
### Purpose
(synq.entities.activity.v1alpha.Purpose)
Purpose is what a service account acts for. It decides whether its reads count toward popularity
when its popularity policy does not say.
| Name | Number | Description |
| - | - | - |
| PURPOSE\_UNSPECIFIED | 0 | none |
| PURPOSE\_LOADER | 1 | Ingestion and replication, such as Fivetran, Airbyte, Stitch or Estuary. |
| PURPOSE\_ORCHESTRATOR | 2 | Airflow, Dagster. |
| PURPOSE\_TRANSFORMER | 3 | dbt, Coalesce Transform, SQLMesh. |
| PURPOSE\_BI | 4 | Looker, Tableau, Sigma, Power BI. |
| PURPOSE\_REVERSE\_ETL | 5 | Hightouch, Census. |
| PURPOSE\_APPLICATION | 6 | A product or service that reads its own data, such as a Streamlit app. |
| PURPOSE\_OBSERVABILITY | 7 | Data quality and observability tools, Coalesce Quality included. |
| PURPOSE\_CATALOG | 8 | Metadata crawlers, Coalesce Catalog included. |
| PURPOSE\_CI | 9 | CI and infrastructure as code. |
| PURPOSE\_OTHER | 10 | none |
### SeriesSplit
(synq.entities.activity.v1alpha.SeriesSplit)
SeriesSplit is how each point of a series is broken down.
| Name | Number | Description |
| - | - | - |
| SERIES\_SPLIT\_UNSPECIFIED | 0 | No breakdown. |
| SERIES\_SPLIT\_ISSUER | 1 | Per issuing tool, from the tag its queries carry. |
### WriteCadence
(synq.entities.activity.v1alpha.WriteCadence)
WriteCadence is how often something wrote an entity in the window, from the median gap between the
hours with writes. It is not freshness.
| Name | Number | Description |
| - | - | - |
| WRITE\_CADENCE\_UNSPECIFIED | 0 | none |
| WRITE\_CADENCE\_NONE | 1 | Nothing written in the window. |
| WRITE\_CADENCE\_ONCE | 2 | One hour with writes. |
| WRITE\_CADENCE\_HOURLY | 3 | none |
| WRITE\_CADENCE\_SUB\_DAILY | 4 | Several times a day. |
| WRITE\_CADENCE\_DAILY | 5 | none |
| WRITE\_CADENCE\_WEEKLY | 6 | none |
| WRITE\_CADENCE\_RARE | 7 | Less often than weekly. |
### AccessSignal
(synq.entities.activity.v1alpha.AccessSignal)
AccessSignal is a place where how an entity is used and what is expected of it disagree. Coalesce
Quality's own access, such as a monitor reading a table, never counts as using it.
| Name | Number | Description |
| - | - | - |
| ACCESS\_SIGNAL\_UNSPECIFIED | 0 | none |
| ACCESS\_SIGNAL\_BUILT\_NEVER\_READ | 1 | A pipeline, an orchestrator's runs or what transforms, wrote the entity in the window, and nobody read it or built anything from it. Each entity carries its share of what building it cost. |
| ACCESS\_SIGNAL\_UNTOUCHED | 2 | A live warehouse table or view nobody read, built from or wrote in the window, with the schema it lives in. A table on a platform with no query history in the window is never untouched, as nothing could have seen it used. |
| ACCESS\_SIGNAL\_READ\_WITHOUT\_VIEWER | 3 | Reads that count toward popularity came through a login that reads for whoever looks at a BI tool or an application, so the viewer behind them is not known. The response sums them per tool. |
| ACCESS\_SIGNAL\_WRITTEN\_OUTSIDE\_PIPELINE | 4 | A pipeline maintains the entity while a person, or a login nobody names, writes it too. |
### ActorGroupKind
(synq.entities.activity.v1alpha.ActorGroupKind)
ActorGroupKind is the group a login's queries land in on the overview. Each lands in exactly one,
so the groups add up to the workspace.
| Name | Number | Description |
| - | - | - |
| ACTOR\_GROUP\_KIND\_UNSPECIFIED | 0 | none |
| ACTOR\_GROUP\_KIND\_COALESCE | 1 | Coalesce Quality's own queries on any login, and every query of a login Coalesce Quality or Coalesce Catalog connects as. |
| ACTOR\_GROUP\_KIND\_SERVICE\_ACCOUNT | 2 | The logins whose first service account is this one, a built-in one included. |
| ACTOR\_GROUP\_KIND\_TOOL | 3 | A tool's tag on queries of logins no service account holds. |
| ACTOR\_GROUP\_KIND\_PEOPLE | 4 | The logins known to be a person's: those a person rule or the platform's email names a person. |
| ACTOR\_GROUP\_KIND\_UNATTRIBUTED | 5 | Queries that name no login. |
| ACTOR\_GROUP\_KIND\_LOGINS | 6 | The logins nothing classifies. Nothing says whether a person or a program is behind one, so it is a login, never a person. |
### ActorKind
(synq.entities.activity.v1alpha.ActorKind)
ActorKind is what an actor of ListActors is.
| Name | Number | Description |
| - | - | - |
| ACTOR\_KIND\_UNSPECIFIED | 0 | none |
| ACTOR\_KIND\_COALESCE | 1 | Coalesce Quality's own queries on any login, and every query of a login Coalesce Quality or Coalesce Catalog connects as. |
| ACTOR\_KIND\_SERVICE\_ACCOUNT | 2 | The logins a service account holds, a built-in one included. |
| ACTOR\_KIND\_TOOL | 3 | A tool, from the tag its queries carry, on any login. |
| ACTOR\_KIND\_PERSON | 4 | The logins of one person. |
| ACTOR\_KIND\_LOGIN | 5 | A login nothing classifies, with all of its queries. |
### ActorSort
(synq.entities.activity.v1alpha.ActorSort)
ActorSort is the order of ListActors, highest first, then by kind and actor.
| Name | Number | Description |
| - | - | - |
| ACTOR\_SORT\_UNSPECIFIED | 0 | Read as ACCESS. |
| ACTOR\_SORT\_ACCESS | 1 | Reads, inputs and writes together. |
| ACTOR\_SORT\_QUERIES | 2 | Queries. Refused under a selection, which counts access, not queries. |
| ACTOR\_SORT\_READS | 3 | none |
| ACTOR\_SORT\_WRITES | 4 | none |
| ACTOR\_SORT\_COMPUTE | 5 | Compute on the asked platform, as each platform reports compute in its own unit. Under a selection, the compute share of the selected entities. |
### FileOperation
(synq.entities.activity.v1alpha.FileOperation)
FileOperation is a query that moves files rather than rows.
| Name | Number | Description |
| - | - | - |
| FILE\_OPERATION\_UNSPECIFIED | 0 | none |
| FILE\_OPERATION\_LOAD | 1 | A COPY that lands files in a table. |
| FILE\_OPERATION\_UNLOAD | 2 | A COPY that writes a table's rows out to files. |
| FILE\_OPERATION\_PUT | 3 | A file uploaded to a stage, such as a Snowflake PUT. |
| FILE\_OPERATION\_GET | 4 | A file downloaded from a stage, such as a Snowflake GET. |
| FILE\_OPERATION\_REMOVE | 5 | Files removed from a stage, such as a Snowflake REMOVE. |
### LoginBasis
(synq.entities.activity.v1alpha.LoginBasis)
LoginBasis is how a login touching an entity is known.
| Name | Number | Description |
| - | - | - |
| LOGIN\_BASIS\_UNSPECIFIED | 0 | none |
| LOGIN\_BASIS\_SERVICE\_ACCOUNT | 1 | A login in a service account. Never a person. |
| LOGIN\_BASIS\_SHARED\_LOGIN | 2 | A login that stands for whoever was looking: one in a BI or application service account, or a login reading for a BI or application tool. |
| LOGIN\_BASIS\_OWN\_LOGIN | 3 | A login linked to a person, by a person link or the platform's email. A platform user alias in synq.users.v1 that names the login wins over that person: the caller applies it. |
| LOGIN\_BASIS\_UNRESOLVED\_LOGIN | 4 | A login nothing classifies, presumed a person. |
| LOGIN\_BASIS\_VIEWER\_EMAIL | 5 | A viewer a BI tool's query tag named by email, on the BI asset it looked at. |
| LOGIN\_BASIS\_VIEWER\_TOOL\_USER | 6 | A viewer a BI tool's query tag named by the tool's own user id, on the BI asset it looked at. |
### UsageEvidence
(synq.entities.activity.v1alpha.UsageEvidence)
UsageEvidence is what a role was learned from, and what its count counts.
| Name | Number | Description |
| - | - | - |
| USAGE\_EVIDENCE\_UNSPECIFIED | 0 | none |
| USAGE\_EVIDENCE\_IDENTITY | 1 | An entity that is the same as the asked one, such as the dbt model that builds a table, with no activity in the window. count is 0. |
| USAGE\_EVIDENCE\_RUNS | 2 | Orchestrated runs. |
| USAGE\_EVIDENCE\_QUERIES | 3 | Warehouse queries. |
| USAGE\_EVIDENCE\_VIEWS | 4 | Opens of a BI asset. |
### UsageLevel
(synq.entities.activity.v1alpha.UsageLevel)
UsageLevel is how precisely a role names its actor, most precise first. A role with an ENTITY or a
SERVICE\_ACCOUNT row has no TOOL row for the same tool.
| Name | Number | Description |
| - | - | - |
| USAGE\_LEVEL\_UNSPECIFIED | 0 | none |
| USAGE\_LEVEL\_ENTITY | 1 | A job, task, step, model or app, in issuer\_entity\_id. One that no longer exists falls back to its tool. |
| USAGE\_LEVEL\_SERVICE\_ACCOUNT | 2 | A service account, a built-in one included, in service\_account\_id. |
| USAGE\_LEVEL\_TOOL | 3 | Only the tool, in issuer, from the tag on its queries. |
| USAGE\_LEVEL\_PEOPLE | 4 | The logins known to be a person's, by a person rule or the platform's email. actors counts the people. |
| USAGE\_LEVEL\_UNKNOWN | 5 | Access with no login and no tool. |
| USAGE\_LEVEL\_LOGINS | 6 | The logins nothing classifies, none of them known to be a person. actors counts the logins. |
### UsageRoleKind
(synq.entities.activity.v1alpha.UsageRoleKind)
UsageRoleKind is what an actor does to an entity, in the order to draw them in, left to right.
| Name | Number | Description |
| - | - | - |
| USAGE\_ROLE\_KIND\_UNSPECIFIED | 0 | none |
| USAGE\_ROLE\_KIND\_LOADED | 1 | A loader writes it, such as Fivetran, Airbyte, Stitch or a loader service account. |
| USAGE\_ROLE\_KIND\_ORCHESTRATED | 2 | An orchestrator runs what builds it, such as an Airflow task, a dbt Cloud job step or a Coalesce job, or an orchestrator writes it. Two orchestrators of one run are two roles, each with its own count. |
| USAGE\_ROLE\_KIND\_TRANSFORMED | 3 | A transformer builds it: a dbt model, a Coalesce node or a SQLMesh model that is the same entity, even with no query history, and a transformer's writes, which add to that model. |
| USAGE\_ROLE\_KIND\_WRITTEN | 4 | Any other write, such as a person's. |
| USAGE\_ROLE\_KIND\_READ | 5 | A tool, an app or people read it. |
| USAGE\_ROLE\_KIND\_REVERSE\_ETL | 6 | A reverse ETL tool, such as Census or Hightouch, reads it to copy the rows into another system, so a broken entity breaks a sync there. |
### CategorySource
(synq.entities.checks.v2.CategorySource)
Where a check's effective category came from. The three sources are a strict
precedence: an admin override beats a producer-explicit category, which beats
a computed one.
| Name | Number | Description |
| - | - | - |
| CATEGORY\_SOURCE\_UNSPECIFIED | 0 | The check has no category on this dimension — it is uncategorised. |
| CATEGORY\_SOURCE\_OVERRIDE | 1 | Admin override — pinned on the check itself through UpsertCheckCategories. Overrides always win. |
| CATEGORY\_SOURCE\_PRODUCER | 2 | Producer-explicit — declared by the tool that produced the check, either directly or as a `quality.check_category` / `quality.governance_category` annotation carried on the source asset. |
| CATEGORY\_SOURCE\_RULE | 3 | Computed — derived from the workspace's categorisation rules. |
### RecategorisationStatus.State
(synq.entities.checks.v2.RecategorisationStatus.State)
| Name | Number | Description |
| - | - | - |
| STATE\_UNSPECIFIED | 0 | none |
| STATE\_IDLE | 1 | No sweep is scheduled or running — the workspace's checks are up to date with its rules. |
| STATE\_SCHEDULED | 2 | A sweep is queued but has not started. |
| STATE\_IN\_PROGRESS | 3 | A sweep is running. |
### CategoryDimension
(synq.entities.checks.v2.CategoryDimension)
The dimension a category classifies a check along. A check carries at most
one category per dimension, and the two resolve independently of each other.
| Name | Number | Description |
| - | - | - |
| CATEGORY\_DIMENSION\_UNSPECIFIED | 0 | none |
| CATEGORY\_DIMENSION\_TECHNICAL | 1 | Technical — what kind of check this is, mechanically. Values such as "nullness", "uniqueness", "referential integrity", "freshness". |
| CATEGORY\_DIMENSION\_GOVERNANCE | 2 | Governance — what the check is for, in data-quality terms. Values such as "Completeness", "Accuracy", "Validity", "Timeliness", "Uniqueness". |
### CategorisationFilter
(synq.entities.checks.v2.CategorisationFilter)
Narrows a listing to checks that do — or do not — have an effective category
on a dimension.
| Name | Number | Description |
| - | - | - |
| CATEGORISATION\_FILTER\_UNSPECIFIED | 0 | No filter. |
| CATEGORISATION\_FILTER\_CATEGORISED | 1 | Only checks that have an effective category on the dimension. |
| CATEGORISATION\_FILTER\_UNCATEGORISED | 2 | Only checks with no effective category on the dimension. |
### OverrideFilter
(synq.entities.checks.v2.OverrideFilter)
Narrows a listing to checks that do — or do not — carry a category set
explicitly on them on a dimension.
| Name | Number | Description |
| - | - | - |
| OVERRIDE\_FILTER\_UNSPECIFIED | 0 | No filter. |
| OVERRIDE\_FILTER\_OVERRIDDEN | 1 | Only checks whose category on the dimension was set explicitly. |
| OVERRIDE\_FILTER\_NOT\_OVERRIDDEN | 2 | Only checks whose category on the dimension was not set explicitly. |
### RuleScope
(synq.entities.checks.v2.RuleScope)
Whether a categorisation rule belongs to the workspace or is one of the
platform-provided defaults.
| Name | Number | Description |
| - | - | - |
| RULE\_SCOPE\_UNSPECIFIED | 0 | none |
| RULE\_SCOPE\_GLOBAL | 1 | Platform-provided rule, shared by every workspace and read-only for workspace users. |
| RULE\_SCOPE\_WORKSPACE | 2 | Rule defined in the caller's workspace. A workspace rule always wins over a matching global rule. |
### PropagationFilter
(synq.entities.checks.v1.PropagationFilter)
PropagationFilter controls which types of checks are included in the coverage response.
| Name | Number | Description |
| - | - | - |
| PROPAGATION\_FILTER\_UNSPECIFIED | 0 | PROPAGATION\_FILTER\_UNSPECIFIED - Default behavior: returns both direct and propagated checks |
| PROPAGATION\_FILTER\_DIRECT\_ONLY | 1 | PROPAGATION\_FILTER\_DIRECT\_ONLY - Returns only direct checks, skips lineage resolution and propagation |
| PROPAGATION\_FILTER\_PROPAGATED\_ONLY | 2 | PROPAGATION\_FILTER\_PROPAGATED\_ONLY - Returns only propagated checks from upstream entities, direct checks arrays will be empty but still present |
### LinkEvidence
(synq.entities.platformusers.v1alpha.LinkEvidence)
LinkEvidence is why a login is suggested for a member, strongest first.
| Name | Number | Description |
| - | - | - |
| LINK\_EVIDENCE\_UNSPECIFIED | 0 | none |
| LINK\_EVIDENCE\_LOGIN\_IS\_EMAIL | 1 | The login is an email address equal to the member's email. |
| LINK\_EVIDENCE\_EMAIL\_LOCAL\_PART | 2 | The login equals the local part of the member's email. |
| LINK\_EVIDENCE\_NAME | 3 | The login spells the member's first and last name. |
### PlatformUserView
(synq.entities.platformusers.v1alpha.PlatformUserView)
PlatformUserView is how much of a login a read returns.
| Name | Number | Description |
| - | - | - |
| PLATFORM\_USER\_VIEW\_UNSPECIFIED | 0 | Read as BASIC. |
| PLATFORM\_USER\_VIEW\_BASIC | 1 | Everything but the signals of each slice. |
| PLATFORM\_USER\_VIEW\_FULL | 2 | With the signals of each slice. |
### SuggestionConfidence
(synq.entities.platformusers.v1alpha.SuggestionConfidence)
SuggestionConfidence is how sure a suggestion is, from its evidence.
| Name | Number | Description |
| - | - | - |
| SUGGESTION\_CONFIDENCE\_UNSPECIFIED | 0 | none |
| SUGGESTION\_CONFIDENCE\_LOW | 1 | A name token or behaviour alone. |
| SUGGESTION\_CONFIDENCE\_MEDIUM | 2 | A tool's tag, a name a tool's setup guide prescribes, a loader's columns on part of the writes, or behaviour with a name token. |
| SUGGESTION\_CONFIDENCE\_HIGH | 3 | Coalesce Quality's own tag, a kind of login the platform states, or a loader's columns on nearly every write. |
### SuggestionSource
(synq.entities.platformusers.v1alpha.SuggestionSource)
SuggestionSource is the evidence a suggestion rests on, most confident first.
| Name | Number | Description |
| - | - | - |
| SUGGESTION\_SOURCE\_UNSPECIFIED | 0 | none |
| SUGGESTION\_SOURCE\_COALESCE\_QUALITY\_MARKER | 1 | Coalesce Quality's tag on the queries of a login Coalesce Quality does not know as its own. |
| SUGGESTION\_SOURCE\_TOOL\_MARKER | 2 | Another tool's tag on the login's queries. |
| SUGGESTION\_SOURCE\_PLATFORM\_KIND | 3 | A kind of login the platform states is not a person, such as Snowflake's `SERVICE`. |
| SUGGESTION\_SOURCE\_NAME\_PATTERN | 4 | A login name a tool's setup guide tells you to create, such as `FIVETRAN_USER`. |
| SUGGESTION\_SOURCE\_LOADER\_FOOTPRINT | 5 | The tables the login writes carry a loader's columns, such as Fivetran's `_fivetran_synced`. |
| SUGGESTION\_SOURCE\_BEHAVIOUR | 6 | The login writes on a fixed cadence, reads through every hour of the day, loads files, or touches far more entities than people do. |
| SUGGESTION\_SOURCE\_NAME\_TOKEN | 7 | A tool's name or a service affix, such as `_SVC`, inside the login name. |
### Classification
(synq.entities.platformusers.v1alpha.Classification)
Classification is how a login reads, in one word.
| Name | Number | Description |
| - | - | - |
| CLASSIFICATION\_UNSPECIFIED | 0 | none |
| CLASSIFICATION\_PERSON | 1 | Linked to a person, by a person link or the email the platform states. |
| CLASSIFICATION\_SERVICE\_ACCOUNT | 2 | In one of the workspace's service accounts. |
| CLASSIFICATION\_BUILT\_IN | 3 | In a built-in account only. |
| CLASSIFICATION\_COALESCE | 4 | A login Coalesce Quality or Coalesce Catalog connects as. |
| CLASSIFICATION\_UNCLASSIFIED | 5 | Nothing classifies it. It is presumed a person, and its reads count. |
### EvidenceKind
(synq.entities.platformusers.v1alpha.EvidenceKind)
EvidenceKind is a kind of hint about what a login is.
| Name | Number | Description |
| - | - | - |
| EVIDENCE\_KIND\_UNSPECIFIED | 0 | none |
| EVIDENCE\_KIND\_TOOL\_MARKER | 1 | A tool's tag on its queries. |
| EVIDENCE\_KIND\_NAME\_PATTERN | 2 | A login name a tool's setup guide tells you to create. |
| EVIDENCE\_KIND\_PLATFORM\_KIND | 3 | A kind of login the platform states is not a person. |
| EVIDENCE\_KIND\_FOOTPRINT | 4 | The tables it writes carry a loader's columns. |
| EVIDENCE\_KIND\_WRITE\_CADENCE | 5 | It writes on a fixed cadence. |
| EVIDENCE\_KIND\_EMAIL | 6 | The platform states an email for it. |
| EVIDENCE\_KIND\_READ\_CADENCE | 7 | It reads through every hour of the day. |
| EVIDENCE\_KIND\_LOAD\_OPERATIONS | 8 | Most of its queries load files or move them on a stage. |
| EVIDENCE\_KIND\_NAME\_TOKEN | 9 | Its name holds a tool's name or a service affix, such as `_SVC`. |
| EVIDENCE\_KIND\_PERSON\_NAME | 10 | Its name reads as a person's, such as `JANE.DOE`, `JANE_DOE` or an email address, and nothing about it says it is a service. A hint about the login, not who it is: the login stays unclassified until a person rule, a stated email or a link names its person. |
### MatchKind
(synq.entities.platformusers.v1alpha.MatchKind)
MatchKind is why a slice is in a service account.
| Name | Number | Description |
| - | - | - |
| MATCH\_KIND\_UNSPECIFIED | 0 | none |
| MATCH\_KIND\_EXACT | 1 | An exact matcher names the login. |
| MATCH\_KIND\_MATCHER | 2 | A matcher's patterns or conditions match it. |
| MATCH\_KIND\_BUILT\_IN | 3 | A built-in account's rules match it. |
### JoinEvidenceKind
(synq.entities.sql\_insights.v1.JoinEvidenceKind)
JoinEvidenceKind says where a join pattern was seen.
| Name | Number | Description |
| - | - | - |
| JOIN\_EVIDENCE\_KIND\_UNSPECIFIED | 0 | none |
| JOIN\_EVIDENCE\_KIND\_SQL\_DEFINITION | 1 | The SQL that builds a table, view or model joins on the key. |
| JOIN\_EVIDENCE\_KIND\_QUERY\_LOG | 2 | Queries in the warehouse's query history made the join. |
### JoinIssuerKind
(synq.entities.sql\_insights.v1.JoinIssuerKind)
JoinIssuerKind says who ran the queries of a JoinIssuer.
| Name | Number | Description |
| - | - | - |
| JOIN\_ISSUER\_KIND\_UNSPECIFIED | 0 | none |
| JOIN\_ISSUER\_KIND\_TOOL | 1 | A tool that the query's tag names. |
| JOIN\_ISSUER\_KIND\_PEOPLE | 2 | People: warehouse users linked to a person, with an email, or not classified otherwise. |
| JOIN\_ISSUER\_KIND\_SERVICE\_ACCOUNT | 3 | Warehouse users in a service account. |
| JOIN\_ISSUER\_KIND\_COALESCE | 4 | The warehouse users Coalesce connects as, for Coalesce Quality or Coalesce Catalog. |
| JOIN\_ISSUER\_KIND\_UNKNOWN | 5 | Queries that name no warehouse user, or users of a recognized tool that no service account holds. |
### JoinKeeps
(synq.entities.sql\_insights.v1.JoinKeeps)
JoinKeeps says which side of a join keeps all of its rows, also those
that find no match, seen from the requested entity's side. A row with no
match comes back with the other side's columns null.
| Name | Number | Description |
| - | - | - |
| JOIN\_KEEPS\_UNSPECIFIED | 0 | A semi or anti join, UPDATE FROM, DELETE USING or MERGE, which return or change the rows of one side only. |
| JOIN\_KEEPS\_MATCHED | 1 | Only the rows that match: an inner join, a comma join, or a cross join with a predicate. |
| JOIN\_KEEPS\_REQUESTED | 2 | All rows of the requested entity: a left outer join from its side, or an ASOF join in which it is the side looking for a match. |
| JOIN\_KEEPS\_OTHER | 3 | All rows of the other entity: a right outer join from the requested entity's side, or an ASOF join in which the other side looks for a match. |
| JOIN\_KEEPS\_BOTH | 4 | All rows of both: a full outer join. |
### JoinType
(synq.entities.sql\_insights.v1.JoinType)
JoinType is a join type as a semantic layer names it.
| Name | Number | Description |
| - | - | - |
| JOIN\_TYPE\_UNSPECIFIED | 0 | none |
| JOIN\_TYPE\_LEFT\_OUTER | 1 | none |
| JOIN\_TYPE\_INNER | 2 | none |
| JOIN\_TYPE\_FULL\_OUTER | 3 | none |
| JOIN\_TYPE\_CROSS | 4 | none |
| JOIN\_TYPE\_OTHER | 5 | `native_join_type` says which. |
### JoinKind
(synq.entities.sql\_insights.v1.JoinKind)
JoinKind is the kind of join a join pair belongs to.
| Name | Number | Description |
| - | - | - |
| JOIN\_KIND\_UNSPECIFIED | 0 | none |
| JOIN\_KIND\_INNER | 1 | INNER JOIN, an unqualified `JOIN`, or MySQL `STRAIGHT_JOIN`. |
| JOIN\_KIND\_LEFT\_OUTER | 2 | LEFT \[OUTER] JOIN. The left side is the one SQL wrote first. |
| JOIN\_KIND\_RIGHT\_OUTER | 3 | RIGHT \[OUTER] JOIN. |
| JOIN\_KIND\_FULL\_OUTER | 4 | FULL \[OUTER] JOIN. |
| JOIN\_KIND\_CROSS | 5 | CROSS JOIN written with a predicate, which some dialects accept. |
| JOIN\_KIND\_LEFT\_SEMI | 6 | LEFT SEMI JOIN. |
| JOIN\_KIND\_RIGHT\_SEMI | 7 | RIGHT SEMI JOIN. |
| JOIN\_KIND\_LEFT\_ANTI | 8 | LEFT ANTI JOIN. |
| JOIN\_KIND\_RIGHT\_ANTI | 9 | RIGHT ANTI JOIN. |
| JOIN\_KIND\_ASOF | 10 | Snowflake / Databricks ASOF JOIN, from its MATCH\_CONDITION or its ON. |
| JOIN\_KIND\_IMPLICIT | 11 | A comma join: `FROM a, b WHERE a.x = b.y`. The predicate comes from the WHERE clause and compares columns of two different FROM items. |
| JOIN\_KIND\_UPDATE\_FROM | 12 | `UPDATE t … FROM s WHERE t.x = s.y`: a WHERE comparison between the updated table and a FROM item. |
| JOIN\_KIND\_DELETE\_USING | 13 | `DELETE FROM t USING s WHERE t.x = s.y`: a WHERE comparison between a table rows are deleted from and a USING item. |
| JOIN\_KIND\_MERGE | 14 | `MERGE INTO t USING s ON t.x = s.y`: a conjunct of the MERGE's ON. The statement then acts on matched and unmatched rows alike, so no outer-join kind describes it. |
### JoinOperator
(synq.entities.sql\_insights.v1.JoinOperator)
JoinOperator is the comparison a join pair makes between its two sides.
| Name | Number | Description |
| - | - | - |
| JOIN\_OPERATOR\_UNSPECIFIED | 0 | none |
| JOIN\_OPERATOR\_EQ | 1 | `=`. |
| JOIN\_OPERATOR\_NULL\_SAFE\_EQ | 2 | A null-safe equality: `<=>` or `IS NOT DISTINCT FROM`. |
| JOIN\_OPERATOR\_NOT\_EQ | 3 | `<>` or `!=`. |
| JOIN\_OPERATOR\_LT | 4 | `<`. |
| JOIN\_OPERATOR\_LTE | 5 | `<=`, and the upper bound of a BETWEEN. |
| JOIN\_OPERATOR\_GT | 6 | `>`. |
| JOIN\_OPERATOR\_GTE | 7 | `>=`, and the lower bound of a BETWEEN. |
| JOIN\_OPERATOR\_OTHER | 8 | Anything else that relates two tables: an OR, a NOT BETWEEN, LIKE, a function of columns of both. The sides then hold the columns each table contributes, and `expression` holds the whole predicate. |
### JoinSideDerivation
(synq.entities.sql\_insights.v1.JoinSideDerivation)
JoinSideDerivation is how a join pair side's column reaches the comparison.
| Name | Number | Description |
| - | - | - |
| JOIN\_SIDE\_DERIVATION\_UNSPECIFIED | 0 | none |
| JOIN\_SIDE\_DERIVATION\_PASSTHROUGH | 1 | The base column arrives unchanged: `a.id`, or a CTE column that selects it as is. |
| JOIN\_SIDE\_DERIVATION\_RENAMED | 2 | Only an alias changed on the way, in a CTE or a subquery: `id AS order_id`. |
| JOIN\_SIDE\_DERIVATION\_TRANSFORMED | 3 | An expression stands between the base column and the comparison: `lower(a.email)`, `left(a.id, 15)`, a CASE, a cast. Evidence of a relationship, never a plain column pair. |
### ExecutionStatus
(synq.entities.executions.v2.ExecutionStatus)
ExecutionStatus represents the outcome of an execution.
| Name | Number | Description |
| - | - | - |
| EXECUTION\_STATUS\_UNSPECIFIED | 0 | none |
| EXECUTION\_STATUS\_OK | 1 | none |
| EXECUTION\_STATUS\_WARN | 2 | none |
| EXECUTION\_STATUS\_ERROR | 3 | none |
| EXECUTION\_STATUS\_CRITICAL | 4 | none |
### ExecutionStatus
(synq.entities.executions.v1.ExecutionStatus)
| Name | Number | Description |
| - | - | - |
| EXECUTION\_STATUS\_UNSPECIFIED | 0 | none |
| EXECUTION\_STATUS\_OK | 1 | none |
| EXECUTION\_STATUS\_WARN | 2 | none |
| EXECUTION\_STATUS\_ERROR | 3 | none |
| EXECUTION\_STATUS\_CRITICAL | 4 | none |
### CllState
(synq.entities.lineage.v1.CllState)
| Name | Number | Description |
| - | - | - |
| CLL\_STATE\_UNSPECIFIED | 0 | Unspecified state. |
| CLL\_STATE\_PARSE\_FAILED | 1 | Parsing of the asset SQL failed. No upstream dependencies can be found. |
| CLL\_STATE\_EXTRACTION\_FAILED | 2 | Extraction of the asset SQL failed. Some unsupported SQL features may be used. Some details might be missing. |
| CLL\_STATE\_RESOLUTION\_FAILED | 3 | Not all columns or tables were found upstream, lineage is not complete. |
| CLL\_STATE\_OK | 10 | No known issues present. |
### NodePosition
(synq.entities.lineage.v1.NodePosition)
| Name | Number | Description |
| - | - | - |
| NODE\_POSITION\_UNSPECIFIED | 0 | none |
| NODE\_POSITION\_START\_NODE | 1 | Node is one of the requested start point. |
| NODE\_POSITION\_UPSTREAM | 2 | Node is upstream of the requested start point. |
| NODE\_POSITION\_DOWNSTREAM | 3 | Node is downstream of the requested start point. |
### LineageDirection
(synq.entities.lineage.v1.LineageDirection)
Direction of the lineage to query.
| Name | Number | Description |
| - | - | - |
| LINEAGE\_DIRECTION\_UNSPECIFIED | 0 | none |
| LINEAGE\_DIRECTION\_UPSTREAM | 1 | none |
| LINEAGE\_DIRECTION\_DOWNSTREAM | 2 | none |
| LINEAGE\_DIRECTION\_UPSTREAM\_DOWNSTREAM | 3 | none |
### UserRole
(synq.users.v1.UserRole)
UserRole is a role a member holds in the workspace. Names match the roles
shown in Settings → Team.
| Name | Number | Description |
| - | - | - |
| USER\_ROLE\_UNSPECIFIED | 0 | none |
| USER\_ROLE\_ADMIN | 1 | Admin: full control of the workspace, including its members. |
| USER\_ROLE\_DEVELOPER | 2 | Developer. |
| USER\_ROLE\_ANALYST | 3 | Analyst. |
| USER\_ROLE\_BUSINESS\_USER | 4 | Business User. |
### UserStatus
(synq.users.v1.UserStatus)
UserStatus is where a member is in the invitation lifecycle of the workspace.
| Name | Number | Description |
| - | - | - |
| USER\_STATUS\_UNSPECIFIED | 0 | none |
| USER\_STATUS\_ACTIVE | 1 | The member can sign in to the workspace. |
| USER\_STATUS\_INVITED | 2 | The member was invited and has not signed in since. |
### GrantRole
(synq.savedviews.v1.GrantRole)
The access level a grant confers on its grantee.
| Name | Number | Description |
| - | - | - |
| GRANT\_ROLE\_UNSPECIFIED | 0 | Unspecified is treated as VIEW. |
| GRANT\_ROLE\_VIEW | 1 | The grantee can see the view. |
| GRANT\_ROLE\_MANAGE | 2 | The grantee can also edit the view and change its sharing/grants. |
### SavedViewConfig.Layout
(synq.savedviews.v1.SavedViewConfig.Layout)
Layout used to render the view's results.
| Name | Number | Description |
| - | - | - |
| LAYOUT\_UNSPECIFIED | 0 | none |
| LAYOUT\_LIST | 1 | none |
| LAYOUT\_CARDS | 2 | none |
| LAYOUT\_TABLE | 3 | none |
### SavedViewContext
(synq.savedviews.v1.SavedViewContext)
The surface a saved view applies to. A view saved on the catalog/assets screen
is not shown on the checks or issues screens, so listing is normally filtered
by the context the caller is rendering.
| Name | Number | Description |
| - | - | - |
| SAVED\_VIEW\_CONTEXT\_UNSPECIFIED | 0 | Catalog / assets — the default surface. |
| SAVED\_VIEW\_CONTEXT\_CHECKS | 1 | Checks / monitors screen. |
| SAVED\_VIEW\_CONTEXT\_ISSUES | 2 | Issues / incidents screen. |
### SavedViewRelationship
(synq.savedviews.v1.SavedViewRelationship)
How a returned saved view relates to the calling user. Output-only: computed
per request from the view's owner, visibility and grants against the caller's
identity. Clients group their "views" UI by this value.
| Name | Number | Description |
| - | - | - |
| SAVED\_VIEW\_RELATIONSHIP\_UNSPECIFIED | 0 | none |
| SAVED\_VIEW\_RELATIONSHIP\_MINE | 1 | The caller owns the view. |
| SAVED\_VIEW\_RELATIONSHIP\_WORKSPACE | 2 | Owned by the workspace itself (e.g. created by a workspace-level token), visible to everyone. |
| SAVED\_VIEW\_RELATIONSHIP\_SHARED\_TO\_WORKSPACE | 3 | Another user's view that they shared with the whole workspace. |
| SAVED\_VIEW\_RELATIONSHIP\_SHARED\_TO\_ME | 4 | Another user's view that was explicitly granted to the caller. |
### SavedViewVisibility
(synq.savedviews.v1.SavedViewVisibility)
Who can see a saved view. This is the absolute sharing state stored on the
view; how it relates to the calling user is reported separately as a
SavedViewRelationship on each returned view.
| Name | Number | Description |
| - | - | - |
| SAVED\_VIEW\_VISIBILITY\_UNSPECIFIED | 0 | Private — only the owner can see it. |
| SAVED\_VIEW\_VISIBILITY\_SHARED | 1 | Shared with the whole workspace — everyone in the workspace can see it. |
| SAVED\_VIEW\_VISIBILITY\_GRANTED | 2 | Granted — the owner plus the users explicitly listed in the view's grants. |
### Dataproduct.Priority
(synq.dataproducts.v2.Dataproduct.Priority)
Priority. P1 is the highest.
| Name | Number | Description |
| - | - | - |
| PRIORITY\_UNSPECIFIED | 0 | none |
| PRIORITY\_P3 | 3 | none |
| PRIORITY\_P2 | 4 | none |
| PRIORITY\_P1 | 5 | none |
### Dataproduct.Source
(synq.dataproducts.v2.Dataproduct.Source)
Where this data product is managed. Products from an external source
(e.g. Atlan) are read-only in the app.
| Name | Number | Description |
| - | - | - |
| SOURCE\_SYNQ | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_ATLAN | 1 | none |
### Dataproduct.Priority
(synq.dataproducts.v1.Dataproduct.Priority)
| Name | Number | Description |
| - | - | - |
| PRIORITY\_UNSPECIFIED | 0 | none |
| PRIORITY\_P3 | 3 | none |
| PRIORITY\_P2 | 4 | none |
| PRIORITY\_P1 | 5 | none |
### Dataproduct.Source
(synq.dataproducts.v1.Dataproduct.Source)
| Name | Number | Description |
| - | - | - |
| SOURCE\_SYNQ | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_ATLAN | 1 | none |
### Owner.Source
(synq.owners.v1.Owner.Source)
Where the owner is managed. Owners from an external source (e.g. Atlan) are
read-only.
| Name | Number | Description |
| - | - | - |
| SOURCE\_SYNQ | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX |
| SOURCE\_ATLAN | 1 | none |
### LineageReplaceMode
(synq.ingest.dwh.v1.LineageReplaceMode)
LineageReplaceMode controls how an ingested batch interacts with lineage that
was ingested earlier, and therefore when previously-ingested edges become
outdated and stop contributing to dependencies.
| Name | Number | Description |
| - | - | - |
| LINEAGE\_REPLACE\_MODE\_UNSPECIFIED | 0 | Treated as MERGE so a producer that forgets to set the mode can only add or refresh edges, never silently expire them. |
| LINEAGE\_REPLACE\_MODE\_MERGE | 1 | MERGE: upsert the lineage of every downstream object present in this batch and leave every other object untouched. Use for incremental refreshes — a single query's lineage, one schema the customer just rebuilt, etc. Nothing is expired; an edge disappears only when the same downstream object is re-ingested without it. |
| LINEAGE\_REPLACE\_MODE\_REPLACE\_SCOPE | 2 | REPLACE\_SCOPE: treat this batch as the authoritative, complete snapshot of all lineage whose downstream object falls inside `scope`. Any previously- ingested edge whose downstream object is inside the scope but absent from this batch becomes outdated and is excluded from dependencies. Use for full periodic refreshes. `scope` MUST be set. |
### LineageObjectDomain
(synq.ingest.dwh.v1.LineageObjectDomain)
LineageObjectDomain classifies what KIND of warehouse object sits at one end
of a lineage edge. It lets the platform tell real table-to-table lineage
apart from edges that touch warehouse-internal or non-tabular objects (load
stages, streams, semantic views, ...), which are rendered differently (or
hidden) in the lineage graph. The values mirror the object domains a
warehouse's native lineage feature reports (for example the domains returned
by Snowflake's GET\_LINEAGE) plus the common equivalents on other warehouses.
Set UNSPECIFIED when the producer cannot determine the domain — the edge is
still stored, just without the table-vs-builtin distinction.
| Name | Number | Description |
| - | - | - |
| LINEAGE\_OBJECT\_DOMAIN\_UNSPECIFIED | 0 | none |
| LINEAGE\_OBJECT\_DOMAIN\_TABLE | 1 | An ordinary table. |
| LINEAGE\_OBJECT\_DOMAIN\_VIEW | 2 | A non-materialized view. |
| LINEAGE\_OBJECT\_DOMAIN\_MATERIALIZED\_VIEW | 3 | A materialized view. |
| LINEAGE\_OBJECT\_DOMAIN\_EXTERNAL\_TABLE | 4 | A table backed by external storage (external/iceberg table, etc.). |
| LINEAGE\_OBJECT\_DOMAIN\_DYNAMIC\_TABLE | 5 | A table refreshed by the warehouse from a query (Snowflake dynamic table, BigQuery materialized view with auto-refresh, ...). |
| LINEAGE\_OBJECT\_DOMAIN\_STREAM | 6 | A change-data-capture object over a table (Snowflake stream, ...). |
| LINEAGE\_OBJECT\_DOMAIN\_STAGE | 7 | A load/unload staging area — warehouse-internal, not a customer table. |
| LINEAGE\_OBJECT\_DOMAIN\_SEMANTIC\_VIEW | 8 | A semantic/metrics layer object (Snowflake semantic view, ...). |
| LINEAGE\_OBJECT\_DOMAIN\_DATASET | 9 | A dataset/grouping object exposed by the warehouse's lineage feature. |
| LINEAGE\_OBJECT\_DOMAIN\_MODULE | 10 | A module/package object exposed by the warehouse's lineage feature. |
| LINEAGE\_OBJECT\_DOMAIN\_OTHER | 99 | A recognised object that does not fit any category above. |
### LineageSource
(synq.ingest.dwh.v1.LineageSource)
LineageSource records HOW the lineage was derived. The platform uses it both
to set the precedence of the resulting edges and to keep lineage of different
origins from silently overwriting one another: declared lineage is additive
to observed lineage, it never replaces it. Pick the single value that best
describes the whole batch.
| Name | Number | Description |
| - | - | - |
| LINEAGE\_SOURCE\_UNSPECIFIED | 0 | none |
| LINEAGE\_SOURCE\_DWH\_NATIVE | 1 | Read from the warehouse's own lineage feature (for example Snowflake SNOWFLAKE.CORE.GET\_LINEAGE). Authoritative, highest fidelity. |
| LINEAGE\_SOURCE\_QUERY\_HISTORY | 2 | Derived by parsing query history / access history. |
| LINEAGE\_SOURCE\_DECLARED | 3 | Declared by the customer or a modelling tool (dbt refs, hand-authored edges, BI relationships) rather than observed in the warehouse. |
| LINEAGE\_SOURCE\_VIEW\_DEFINITION | 4 | Derived from a view or materialization definition. |
### SqlObfuscationMode
(synq.ingest.dwh.v1.SqlObfuscationMode)
SqlObfuscationMode represents the level of SQL obfuscation applied to query logs.
This is critical for on-premise deployments where customers want to prevent
sensitive data in SQL queries from being sent to Coalesce Quality backend.
| Name | Number | Description |
| - | - | - |
| SQL\_OBFUSCATION\_MODE\_NONE | 0 | buf:lint:ignore ENUM\_ZERO\_VALUE\_SUFFIX No obfuscation was applied |
| SQL\_OBFUSCATION\_MODE\_REDACT\_LITERALS | 1 | String and numeric literals were replaced with placeholders while preserving query structure for SQL parsing |
### DatacheckStatus
(synq.datachecks.v1.DatacheckStatus)
Result of a datacheck execution
| Name | Number | Description |
| - | - | - |
| DATACHECK\_STATUS\_UNSPECIFIED | 0 | none |
| DATACHECK\_STATUS\_PASSED | 1 | Check passed successfully. |
| DATACHECK\_STATUS\_FAILED | 2 | Check failed due to violations. |
| DATACHECK\_STATUS\_ERROR | 3 | Check failed due to error in check itself. |
| DATACHECK\_STATUS\_SKIPPED | 4 | Check was skipped (e.g. not applicable) |
### Severity
(synq.datachecks.sqltests.v1.Severity)
| Name | Number | Description |
| - | - | - |
| SEVERITY\_UNSPECIFIED | 0 | none |
| SEVERITY\_WARNING | 1 | none |
| SEVERITY\_ERROR | 2 | none |
| SEVERITY\_INFO | 3 | none |
### SqlTestDeploymentRuleType
(synq.datachecks.sqltests.v1.SqlTestDeploymentRuleType)
SqlTestDeploymentRuleType identifies which kind of selection a deployment rule
uses. The value corresponds to the populated `config` oneof arm on
SqlTestDeploymentRule / SqlTestDeploymentRuleView. Use it with
ListSqlTestDeploymentRulesRequest.rule\_types to return only rules of selected
kinds.
| Name | Number | Description |
| - | - | - |
| SQL\_TEST\_DEPLOYMENT\_RULE\_TYPE\_UNSPECIFIED | 0 | No filter — when used in a request, rules of any type are returned. |
| SQL\_TEST\_DEPLOYMENT\_RULE\_TYPE\_QUERY | 1 | Rule with `query_config`: selects tables/views by query and deploys tests on the matches. |
| SQL\_TEST\_DEPLOYMENT\_RULE\_TYPE\_EXCLUSIVE\_QUERY | 2 | Rule with `exclusive_query_config`: a query whose matches are exclusively owned by this rule. |
| SQL\_TEST\_DEPLOYMENT\_RULE\_TYPE\_STATIC | 3 | Rule with `static_config`: targets a single fixed table or view. |
| SQL\_TEST\_DEPLOYMENT\_RULE\_TYPE\_EXCLUSIVE\_STATIC | 4 | Rule with `exclusive_static_config`: a single fixed table or view, exclusively owned. |
### SqlTestSkipReason
(synq.datachecks.sqltests.v1.SqlTestSkipReason)
SqlTestSkipReason is the checkable form of the `reason` string carried by
SqlTestsDeploymentOverview\.SkippedTable and .SkippedTest, mirroring the
SCREAMING\_SNAKE tokens documented on those fields one-for-one. It also carries
the reason a SqlTestDeploymentChange deletes a test, which has no string form.
The set is open by design — a producer may report a reason before this enum has
a matching value for it — so a client must treat SQL\_TEST\_SKIP\_REASON\_UNSPECIFIED
(and any value this client predates) as "skipped, reason unrecognised" rather
than as proof nothing applies.
| Name | Number | Description |
| - | - | - |
| SQL\_TEST\_SKIP\_REASON\_UNSPECIFIED | 0 | none |
| SQL\_TEST\_SKIP\_REASON\_HIGHER\_PRIORITY\_RULE | 1 | none |
| SQL\_TEST\_SKIP\_REASON\_NO\_INTEGRATION | 2 | none |
| SQL\_TEST\_SKIP\_REASON\_TEST\_PREPARATION\_FAILED | 3 | none |
| SQL\_TEST\_SKIP\_REASON\_TEST\_OWNED\_BY\_OTHER\_RULE | 4 | none |
| SQL\_TEST\_SKIP\_REASON\_MISSING\_COLUMNS | 5 | The table's schema is known and lacks a column the test reads. |
| SQL\_TEST\_SKIP\_REASON\_SCHEMA\_UNAVAILABLE | 6 | No schema is known for the table yet, so column tests cannot be checked. |
# Examples
Source: https://docs.synq.io/api-reference/examples
## Examples
You can find some language specific examples in the linked pages.
#### golang
* [1\_create\_types](examples/golang/1_create_types)
* [2\_create\_entities](examples/golang/2_create_entities)
* [3\_create\_features](examples/golang/3_create_features)
* [alerts\_management](examples/golang/alerts_management)
* [am\_deployment\_rules](examples/golang/am_deployment_rules)
* [atlan\_integration](examples/golang/atlan_integration)
* [bi\_tool\_lineage](examples/golang/bi_tool_lineage)
* [create\_dataproduct](examples/golang/create_dataproduct)
* [datachecks](examples/golang/datachecks)
* [deliveries](examples/golang/deliveries)
* [entity\_incidents](examples/golang/entity_incidents)
* [entity\_issues](examples/golang/entity_issues)
* [entity\_schema](examples/golang/entity_schema)
* [ingest\_dbt](examples/golang/ingest_dbt)
* [ingest\_sqlmesh](examples/golang/ingest_sqlmesh)
* [integrations\_management](examples/golang/integrations_management)
* [list\_sql\_tests](examples/golang/list_sql_tests)
* [manage\_sql\_tests](examples/golang/manage_sql_tests)
* [monitors\_history](examples/golang/monitors_history)
* [monitors\_predictions](examples/golang/monitors_predictions)
* [omni\_types](examples/golang/omni_types)
* [owners\_ownership](examples/golang/owners_ownership)
* [replication\_lineage](examples/golang/replication_lineage)
* [token\_auth](examples/golang/token_auth)
#### http
* [users\_management](examples/http/users_management)
#### python
* [alerts\_management](examples/python/alerts_management)
* [bi\_tool\_lineage](examples/python/bi_tool_lineage)
* [ingest\_airflow\_logs](examples/python/ingest_airflow_logs)
* [integrations\_management](examples/python/integrations_management)
* [list\_sql\_tests](examples/python/list_sql_tests)
* [owners\_ownership](examples/python/owners_ownership)
* [users\_management](examples/python/users_management)
# Getting Started
Source: https://docs.synq.io/api-reference/getting-started
Getting started with developer API at Coalesce Quality
# Overview
The Coalesce Quality API is available for developers to manage certain functionalities using custom workflows. Coalesce Quality exposes its API as [gRPC](https://grpc.io/) services. This means that the API are as easy to use as calling functions from your code.
To use the API, you need to do the following.
1. Clone protos from our github repository and generate the client code in the language of your choice.
2. Generate an access token and use it to connect to Coalesce Quality API.
3. Initialize clients and call functions in your code.
You can find language specific examples [here](https://github.com/getsynq/api/tree/main/examples).
# Client Code
The simplest way to use Coalesce Quality API is to use the SDKs from our [`buf` repository](https://buf.build/getsynq/api/sdks). Use select the language of your choice and follow the instructions to add the Coalesce Quality API to your project.
## Generating client code
If you prefer to, the client code can be generated from the protos available at our [github repository](https://github.com/getsynq/api).
```bash theme={null}
$ git clone git@github.com:getsynq/api.git
```
[gRPC](https://grpc.io/) supports a wide choice of languages and you can find the necessary guides on how to get started in a language of your choice [here](https://grpc.io/docs/languages/). You don't need to understand it all to get started with Coalesce Quality API. You just need to find the right tools to build the client code in your language of choice.
Additionally, you will need to install [protovalidate](https://github.com/bufbuild/protovalidate) in the language of your choice.
Following are some language references.
## Go
You will need the following plugins to generate golang code from the protos.
```bash theme={null}
$ go install google.golang.org/protobuf/cmd/protoc-gen-go
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
```
If you are starting off with gRPC and protos, it might be useful to follow the guide [here](https://grpc.io/docs/languages/go/quickstart/).
Run the following command from `` to generate the code in golang.
```bash theme={null}
$ protoc --proto_path=./protos --go_out=./gen2 --go-grpc_out=./gen protos/**/*.proto
```
The generated code is added to the `./gen` folder. You can change the location or find more options [here](https://protobuf.dev/reference/go/go-generated/) on how to use the `protoc` generator to suit your project's needs.
## Python
You will need the following tools to generate python code from the protos.
```bash theme={null}
$ python -m pip install grpcio
$ python -m pip install grpcio-tools
```
If you are starting off with gRPC and protos, it might be useful to follow the guide [here](https://grpc.io/docs/languages/python/quickstart/).
Run the following command from `` to generate the code in golang.
```bash theme={null}
$ python3 -m grpc_tools.protoc -Iprotos --python_out=./gen --pyi_out=./gen --grpc_python_out=./gen protos/**/*.proto
```
The generated code is added to the `./gen` folder. You can change the location to suit your project's needs.
# Fetching Access Token
You need a valid access token to communicate with the Coalesce Quality servers. To generate the access token, you need client credentials.
You can generate an client credentials (`CLIENT_ID` and `CLIENT_SECRET`) from the [Coalesce Quality application](https://app.synq.io/settings/api). The credentials are scoped so make sure to select the one best suited to execute the RPCs that you wish to.
You can now fetch the token source by making the following `POST` call to our OAuth2 server.
```bash theme={null}
# For other regions: api.us.synq.io for US, api.au.synq.io for AU.
curl -d "client_id=&client_secret=&grant_type=client_credentials" -X POST https://developer.synq.io/oauth2/token
```
The response will have the following structure.
```json theme={null}
{
"access_token": ,
"expires_in": ,
...
}
```
The `` thus fetched is a valid JWT token which should be passed on to the calls made to Coalesce Quality API.
# Examples
The language specific examples to use Coalesce Quality APIs can be found [here](https://docs.synq.io/api-reference/examples).
# Troubleshooting
* If you experience a considerable slowness in connecting to the gRPC services, please flush your DNS cache. This is specially relevant if you are behind a VPN.
```
dscacheutil -flushcache
killall -HUP mDNSResponder
```
# API Scopes
Source: https://docs.synq.io/api-reference/scopes
Scopes that can be granted to Coalesce Quality API tokens and OAuth clients, and the public API methods each one unlocks
Every Coalesce Quality API token and OAuth client credential is granted a set of **scopes**. A scope authorises a group of related API methods; a call succeeds when the token carries **at least one** of the scopes required by that method (OR semantics).
Choose the narrowest set of scopes that covers the methods your integration calls. Scopes are selected when you create a token or client in [API settings](https://app.synq.io/settings/api), in the order listed below.
There are 62 scopes available to API tokens and OAuth clients. Internal scopes reserved for managed integrations are not listed here.
## Edit Issues
`SCOPE_ISSUES_EDIT`
Create and update data quality issues, including changing their status and assignees.
| Method | Description |
| - | - |
| `MarkExpected` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-expected` | Mark issue as expected. |
| `MarkFixed` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-fixed` | Mark issue as fixed. |
| `MarkInvestigating` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-investigating` | Mark issue as being investigated. |
| `MarkNoActionNeeded` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-no-action-needed` | Mark issue as no action needed. |
| `PostComment` *synq.issues.issues.v1.IssuesService* `POST /api/issues/v1/{issue_id}/comment` | Post a comment on an issue. |
| `ClearStatus` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/clear-status` | Clear the triage status of an issue, returning it to an untriaged state. |
| `DeleteComment` *synq.issues.v2.IssuesService* `DELETE /api/issues/v2/{issue_id}/comment/{comment_id}` | Delete a comment on an issue. Only the actor who posted the comment may |
| `MarkExpected` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-expected` | Mark issue as expected. |
| `MarkFixed` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-fixed` | Mark issue as fixed. |
| `MarkInvestigating` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-investigating` | Mark issue as being investigated. |
| `MarkNoActionNeeded` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-no-action-needed` | Mark issue as no action needed. |
| `PostComment` *synq.issues.v2.IssuesService* `POST /api/issues/v2/{issue_id}/comment` | Post a comment on an issue. |
## Read Issues
`SCOPE_ISSUES_READ`
View data quality issues and their details.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchGetIssues` *synq.issues.issues.v1.IssuesService* `POST /api/issues/v1/by-id` | |
| `ListIssues` *synq.issues.issues.v1.IssuesService* `GET /api/issues/v1` | |
| `BatchGetIssues` *synq.issues.v2.IssuesService* `POST /api/issues/v2/by-id` | |
| `ListIssues` *synq.issues.v2.IssuesService* `GET /api/issues/v2` | |
## Edit Incidents
`SCOPE_INCIDENTS_EDIT`
Declare, update, and resolve incidents raised for confirmed issues with business impact.
| Method | Description |
| - | - |
| `AddIssuesToIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/issues` | |
| `AssignIncidentOwner` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/assign-owner` | |
| `CancelIncident` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/cancel` | Cancel an incident |
| `CloseIncident` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/close` | Close an incident |
| `CreateIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1` | |
| `DeleteComment` *synq.incidents.v1.IncidentsService* `DELETE /api/incidents/v1/{incident_id}/comment/{comment_id}` | Delete a comment on an incident. Only the actor who posted the comment may |
| `MergeIncidents` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/merge` | |
| `PostComment` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/comment` | Post a comment on an incident. |
| `RemoveIssuesFromIncident` *synq.incidents.v1.IncidentsService* `DELETE /api/incidents/v1/{incident_id}/issues` | |
| `RenameIncident` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/rename` | |
| `SplitIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/split` | |
| `UpdateIncidentState` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/state` | |
## Read Incidents
`SCOPE_INCIDENTS_READ`
View incidents, including their status and the issues they track.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchGetIncidents` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/by-id` | |
| `ListIncidents` *synq.incidents.v1.IncidentsService* `GET /api/incidents/v1` | |
## Edit Entities
`SCOPE_ENTITY_EDIT`
Create and update entities such as tables, dashboards, and jobs in the catalog.
| Method | Description |
| - | - |
| `DeleteCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `DELETE /api/entities/custom/v1/check-relationships` | Delete relationships between checks and the entities they validate. |
| `UpsertCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `POST /api/entities/custom/v1/check-relationships` | Create or update relationships that attach a check (e.g. a SQL test) to the |
| `DeleteEntity` *synq.entities.custom.v1.EntitiesService* `DELETE /api/entities/custom/v1/entities` | Delete an entity by its identifier. If the entity does not exist, it will be a no-op. |
| `UpsertEntity` *synq.entities.custom.v1.EntitiesService* `POST /api/entities/custom/v1/entities` | Create or update an entity. If the entity does not exist, it will be created, otherwise it will be u... |
| `DeleteEntityFeature` *synq.entities.custom.v1.FeaturesService* `DELETE /api/entities/custom/v1/features` | Delete an entity by its identifier. If the entity does not exist, it will be a no-op. |
| `UpsertEntityFeature` *synq.entities.custom.v1.FeaturesService* `POST /api/entities/custom/v1/features` | Create or update an entity. If the entity does not exist, it will be created, otherwise it will be u... |
| `DeleteEntitiesGroup` *synq.entities.custom.v1.GroupsService* `DELETE /api/entities/custom/v1/groups` | DeleteEntitiesGroup deletes a group by its identifier. If the group does not exist, it will be a no-... |
| `UpsertEntitiesGroup` *synq.entities.custom.v1.GroupsService* `POST /api/entities/custom/v1/groups` | UpsertEntitiesGroup updates current definition of group to the server. As part of the upsert operati... |
## Read Entities
`SCOPE_ENTITY_READ`
View entities such as tables, dashboards, and jobs, along with their metadata.
| Method | Description |
| - | - |
| `GenerateConnectionsYaml` *synq.agent.dwh.v1.ConnectionsService* | GenerateConnectionsYaml converts data warehouse connections to YAML configuration format. |
| `ListConnections` *synq.agent.dwh.v1.ConnectionsService* | ListConnections retrieves data warehouse connections for a workspace. |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchGetEntityUsage` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/usage` | Read how each asked entity is used over the window, in one line: what loads it, what runs and |
| `BatchGetPopularity` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/popularity` | Read how popular each asked entity is over the window: its counted reads and writes per measure, |
| `GetActivityOverview` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/overview` | Read a workspace's queries and their cost per group of logins, per platform, and its most read |
| `GetEntityActivitySeries` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/series` | Read how access to one entity moves over the window: per direction, a point per UTC hour, a point |
| `ListAccessSignals` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/signals` | List the entities of the workspace, or of a selection, that one signal names, such as the tables |
| `ListActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-list` | List the workspace's actors, the service accounts, tools, people, logins nothing classifies and |
| `BatchGetEntityAnnotations` *synq.entities.annotations.v1.AnnotationsService* `POST /api/entities/annotations/v1/batchGet` | Read the annotations carried by specific entities. |
| `ListAnnotations` *synq.entities.annotations.v1.AnnotationsService* `GET /api/entities/annotations/v1` | List all available annotations with their usage counts across entities. |
| `BatchChange` *synq.entities.changes.v1.ChangesService* `POST /api/changes/v1/by-id` | BatchChange retrieves multiple entity changes by their IDs in a single request. |
| `List` *synq.entities.changes.v1.ChangesService* `GET /api/changes/v1` | Retrieves the complete change history for specified entities. |
| `GetCoverage` *synq.entities.checks.v1.ChecksCoverageService* | GetCoverage retrieves the coverage of checks for a specific entity. |
| `GetTableConstraints` *synq.entities.constraints.v1.TableConstraintsService* `GET /api/constraints/v1/table-constraints` | Get table constraints (primary keys, foreign keys, indexes, etc.) for a given entity. |
| `BatchDatabaseCoordinates` *synq.entities.coordinates.v1.DatabaseCoordinatesService* `POST /api/coordinates/v1/database/by-id` | Get multiple DatabaseCoordinates for identifiers. |
| `BatchIdsByCoordinates` *synq.entities.coordinates.v1.DatabaseCoordinatesService* `POST /api/coordinates/v1/database/by-fqn` | Given database FQN return possible entity ids. |
| `ListCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `POST /api/entities/custom/v1/check-relationships/list` | List every relationship that attaches a check to the entities it |
| `ListEntities` *synq.entities.custom.v1.EntitiesService* `GET /api/entities/custom/v1/entities` | List all entities. |
| `ListEntityFeatures` *synq.entities.custom.v1.FeaturesService* `POST /api/entities/custom/v1/features/list` | List the features of one entity, or of every custom entity in the |
| `ListEntityGroups` *synq.entities.custom.v1.GroupsService* `POST /api/entities/custom/v1/groups/list` | ListEntityGroups lists all groups. |
| `ListTypes` *synq.entities.custom.v1.TypesService* `POST /api/entities/custom/v1/types/list` | List all entities. |
| `BatchGetEntities` *synq.entities.entities.v1.EntitiesService* `POST /api/entities/v1/by-id` | Get multiple entities by their identifiers. |
| `GetEntity` *synq.entities.entities.v1.EntitiesService* | Get specific entity by its identifier. |
| `ListEntities` *synq.entities.entities.v1.EntitiesService* `GET /api/entities/v1` | List entities matching the given filter |
| `SearchEntities` *synq.entities.entities.v1.EntitiesService* `GET /api/entities/v1/search` | Search for entities. |
| `Browse` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/browse` `GET /api/entities/folders/v1/browse` | Browse the hierarchy one level at a time. With no `parent`, returns the |
| `GetFolderOf` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/folder-of` `GET /api/entities/folders/v1/folder-of` | Given entity ids, return the folder breadcrumb(s) each entity lives in — |
| `ListMembers` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/members` `GET /api/entities/folders/v1/members` | List the entity ids contained in a folder, paginated. Direct members only by |
| `GetImpact` *synq.entities.impact.v1.ImpactService* `POST /api/impact/v1` | |
| `BatchResolveIdentifiers` *synq.entities.resolve.v1.IdentifierResolveService* `POST /api/resolve/v1/by-id` | Resolve multiple identifiers, returning their Coalesce Quality paths and identity groups. |
| `BatchSchemaMismatches` *synq.entities.schemas.v1.SchemaMismatchesService* `POST /api/schema-mismatches/v1` | Get schema mismatches for a batch of entities by their identifiers. |
| `GetSchema` *synq.entities.schemas.v1.SchemasService* `GET /api/schema/v1` | Get schema of the entity its identifier. |
| `BatchSqlDetails` *synq.entities.sql\_insights.v1.SqlInsightsService* `POST /api/sql-insights/v1/details:batch` | BatchSqlDetails returns the SQL details for each of the requested entities, |
| `ListJoinPatterns` *synq.entities.sql\_insights.v1.SqlInsightsService* `GET /api/sql-insights/v1/join-patterns` | ListJoinPatterns lists how other SQL joins a table, or one of its columns: |
| `ListSqlConstructs` *synq.entities.sql\_insights.v1.SqlInsightsService* `GET /api/sql-insights/v1/constructs` | ListSqlConstructs returns the distribution of SQL constructs across all |
| `ListEntityTraitDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/entity-traits/list` | List every trait a selection query can filter entities by. |
| `ListEntityTypeDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/entity-types/list` | List every kind of entity in your workspace, the built-in types and your own. |
| `ListExecutionTypeDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/execution-types/list` | List every kind of run Coalesce Quality records against an entity. |
| `ListPlatformDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/platforms/list` | List every platform Coalesce Quality connects to, with its display name. |
| `GetDiffByCommit` *synq.git.commits.v1.CommitsService* `GET /api/git/v1/commit/{commit_sha}` | |
| `BatchGetEntityKnowledge` *synq.knowledge.v1alpha.KnowledgeService* `POST /api/knowledge/v1alpha/entities/by-id` | Read the knowledge held about entities: the description people wrote for |
| `BatchGetKnowledgePages` *synq.knowledge.v1alpha.KnowledgeService* `POST /api/knowledge/v1alpha/pages/by-id` | Read knowledge pages by id: their content, where they sit in the tree, |
| `ListKnowledgePageLinks` *synq.knowledge.v1alpha.KnowledgeService* `GET /api/knowledge/v1alpha/pages/{page_id}/links` | List what is pinned to a knowledge page, in either direction: the ids of |
| `ListKnowledgePages` *synq.knowledge.v1alpha.KnowledgeService* `GET /api/knowledge/v1alpha/pages` | List the ids of knowledge pages, ordered by name, optionally narrowed by a |
| `BatchGetLatestEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `POST /api/monitors/predictions/v1/entity-metrics:batchLatest` | Returns the most recent observed table-stats sample (row count, freshness) for each |
| `GetEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1/entity-metrics` | Returns raw metric timeseries for an entity. Identity-aware: resolves |
| `GenerateQuery` *synq.queries.v1.NLQueryService* `POST /api/queries/v1/generate` | GenerateQuery generates a Query from a natural language description. |
| `ResolveQuery` *synq.queries.v1.QueriesService* `POST /api/queries/v1/resolve` | Evaluate a selection and return the matched entity ids, paginated. Pass a |
| `TranslateResolverQl` *synq.queries.v1.QueriesService* `POST /api/queries/v1/translate-resolver-ql` | Compile a ResolverQL expression into a structured public `Query`. Unlike |
## Edit Entity Types
`SCOPE_ENTITY_TYPE_EDIT`
Define and update custom entity types.
| Method | Description |
| - | - |
| `DeleteType` *synq.entities.custom.v1.TypesService* `DELETE /api/entities/custom/v1/types` | Delete an entity by its identifier. If the entity does not exist, it will be a no-op. |
| `UpsertType` *synq.entities.custom.v1.TypesService* `POST /api/entities/custom/v1/types` | Create or update an entity. If the entity does not exist, it will be created, otherwise it will be u... |
## Read Entity Types
`SCOPE_ENTITY_TYPE_READ`
View entity type definitions.
| Method | Description |
| - | - |
| `ListTypes` *synq.entities.custom.v1.TypesService* `POST /api/entities/custom/v1/types/list` | List all entities. |
| `ListEntityTraitDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/entity-traits/list` | List every trait a selection query can filter entities by. |
| `ListEntityTypeDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/entity-types/list` | List every kind of entity in your workspace, the built-in types and your own. |
| `ListExecutionTypeDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/execution-types/list` | List every kind of run Coalesce Quality records against an entity. |
| `ListPlatformDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/platforms/list` | List every platform Coalesce Quality connects to, with its display name. |
## Edit Lineage
`SCOPE_LINEAGE_EDIT`
Create and update lineage relationships between entities.
| Method | Description |
| - | - |
| `DeleteRelationships` *synq.entities.custom.v1.RelationshipsService* `DELETE /api/entities/custom/v1/relationships` | Delete relationships between entities. If the relationship does not exist, it will be ignored. |
| `UpsertRelationships` *synq.entities.custom.v1.RelationshipsService* `POST /api/entities/custom/v1/relationships` | Create or update relationships between entities. If the relationship already exists, it will be upda... |
| `CreateFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha` | Create a fold. Repeating the same request returns the fold it created; |
| `DeleteFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/delete` | Delete a fold. Deleting a fold that does not exist succeeds. |
| `RenameFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/rename` | Change the name shown on a fold's node. |
| `SetFoldEnabled` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/enabled` | Switch a fold on or off. A fold switched off is kept and listed, and |
| `UpdateFoldMatcher` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/matcher` | Change which entities a fold matches. The fold's node keeps its entity id. |
## Read Lineage
`SCOPE_LINEAGE_READ`
View upstream and downstream lineage between entities.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchGetEntityUsage` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/usage` | Read how each asked entity is used over the window, in one line: what loads it, what runs and |
| `GetActorSummary` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-summary` | Read what one actor read, built and wrote over the window, what its queries cost per compute and |
| `ListEntityActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actors` | Read who read and wrote each asked entity over the window: the jobs, the service accounts, the |
| `ListRelationships` *synq.entities.custom.v1.RelationshipsService* `POST /api/entities/custom/v1/relationships/list` | List relationships touching the given entities, or every relationship in |
| `GetImpact` *synq.entities.impact.v1.ImpactService* `POST /api/impact/v1` | |
| `BatchGetFoldNodes` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/nodes/batch-get` | Read fold nodes by entity id: which fold a node in a lineage response |
| `BatchGetFolds` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/batch-get` | Read folds by id. An id with no fold is absent from the response. |
| `ListFolds` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `GET /api/lineage/folds/v1alpha` | List the ids of every fold in the workspace, switched off ones included, |
| `PreviewFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/preview` | Show what a matcher would fold, before it is saved: how many entities and |
| `GetLineage` *synq.entities.lineage.v1.LineageService* `POST /api/lineage/v1` | |
| `BatchOrchestration` *synq.entities.orchestration.v1.OrchestrationService* `GET /api/orchestration/v1` | BatchOrchestration returns, for each requested entity, the jobs, job steps and tasks that ran it |
## Read Entity Code
`SCOPE_CODE_READ`
View the SQL or source code associated with an entity.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `ListCode` *synq.entities.code.v1.CodeService* `GET /api/code/v1` | ListCode retrieves all code artifacts associated with a specific entity. |
| `GetDiffByChangeId` *synq.git.commits.v1.CommitsService* `GET /api/git/v1/change/{change_id}` | |
## Read Entity Status
`SCOPE_STATUS_READ`
View the health and status of entities, including status summaries and history.
| Method | Description |
| - | - |
| `BatchGetIncidents` *synq.entities.status.v1.EntityIncidentsService* `POST /api/status/v1/incidents/by-id` | Get multiple entity statuses by their identifiers. |
| `GetIncidents` *synq.entities.status.v1.EntityIncidentsService* | Get specific entity status by its identifier. |
| `BatchGetIssuesStatus` *synq.entities.status.v1.EntityIssuesService* `POST /api/status/v1/issues/by-id` | Get multiple entity statuses by their identifiers. |
| `GetIssuesStatus` *synq.entities.status.v1.EntityIssuesService* | Get specific entity status by its identifier. |
## Edit SQL Tests
`SCOPE_DATACHECKS_SQLTESTS_EDIT`
Create, update, and delete SQL tests.
| Method | Description |
| - | - |
| `BatchDeleteSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `DELETE /api/datachecks/sql-test-deployment-rules/v1/batch` | |
| `BatchDeploySqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/batch/deployed` | |
| `DeleteSqlTestDeploymentRule` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `DELETE /api/datachecks/sql-test-deployment-rules/v1/{id}` | |
| `DeploySqlTestDeploymentRule` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/deployed` | Deploy (create or update) a SqlTestDeploymentRule and immediately sync tests |
| `BatchDeleteSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `DELETE /api/datachecks/sqltests/v1` | Delete SqlTests by their IDs. |
| `BatchUpsertSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `POST /api/datachecks/sqltests/v1` | Upsert SqlTests based on a unique ID. |
## Read SQL Tests
`SCOPE_DATACHECKS_SQLTESTS_READ`
View SQL tests and their configuration.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchGetSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/by-id` | |
| `GetSqlTestDeployDeleteOverview` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1/{id}/deleted` | Preview the effect of deleting a rule, without deleting it. |
| `GetSqlTestDeployOverview` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1/overview` | Preview the effect of deploying a rule (which have not yet been saved), without |
| `ListSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1` | |
| `BatchGetSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `POST /api/datachecks/sqltests/v1/by-id` | Get SqlTests by their IDs. |
| `ListSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `GET /api/datachecks/sqltests/v1` | List SqlTests for given annotations. |
## Read SQL Tests Audit
`SCOPE_DATACHECKS_SQLTESTS_AUDIT_READ`
View the audit history of changes to SQL tests.
| Method | Description |
| - | - |
| `GetSqlTestAudit` *synq.datachecks.sqltests.v1.SqlTestsService* | Get SqlTest audit from a given execution ID in jwt token. |
## Edit Test Suggestions
`SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT`
Accept, dismiss, and manage suggested tests.
| Method | Description |
| - | - |
| `MarkAsCancelled` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `MarkAsCompleted` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `MarkAsFailed` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `MarkAsRunning` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `SuggestFor` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `BatchAcceptTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `PATCH /api/datachecks/testsuggestions/v1/accepted` | |
| `BatchCreateTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `POST /api/datachecks/testsuggestions/v1` | |
| `BatchRejectTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `PATCH /api/datachecks/testsuggestions/v1/rejected` | |
## Read Test Suggestions
`SCOPE_DATACHECKS_TESTSUGGESTIONS_READ`
View suggested tests.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchSuggestionStates` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `GetActiveSuggestionStatesFor` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `ListSuggestionStates` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `BatchGetTestSuggestionsForEntities` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `GET /api/datachecks/testsuggestions/v1` | |
## Trigger Data Checks
`SCOPE_DATACHECKS_TRIGGER`
Trigger data checks to run on demand.
| Method | Description |
| - | - |
| `TriggerDatachecks` *synq.datachecks.v1.TriggerService* `POST /api/datachecks/scheduler/v1/trigger` | TriggerDatachecks executes datachecks for the specified entities. |
## Edit Executions
`SCOPE_EXECUTION_EDIT`
Report execution runs and their results.
| Method | Description |
| - | - |
| `UpsertExecution` *synq.entities.custom.v1.EntityExecutionsService* `POST /api/entities/custom/executions/v1` | Execution affects status of the custom entity |
| `UpsertLogEntry` *synq.entities.custom.v1.EntityExecutionsService* `POST /api/entities/custom/executions/v1/log-entry` | LogEntry is a log message for the custom entity |
| `UpsertExecution` *synq.entities.executions.v1.EntityExecutionsService* `POST /api/executions/v1` | Execution affects status of the custom entity |
| `UpsertLogEntry` *synq.entities.executions.v1.EntityExecutionsService* `POST /api/executions/v1/log-entry` | LogEntry is a log message for the custom entity |
## Edit Execution Logs
`SCOPE_EXECUTION_LOG_EDIT`
Write logs for execution runs.
| Method | Description |
| - | - |
| `UpsertLogEntry` *synq.entities.custom.v1.EntityExecutionsService* `POST /api/entities/custom/executions/v1/log-entry` | LogEntry is a log message for the custom entity |
| `UpsertLogEntry` *synq.entities.executions.v1.EntityExecutionsService* `POST /api/executions/v1/log-entry` | LogEntry is a log message for the custom entity |
## Read Executions
`SCOPE_EXECUTION_READ`
View execution run history and results.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `BatchGetExecutions` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/by-id` | BatchGetExecutions retrieves multiple executions by their IDs. |
| `GetExecutionsSummary` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/summary` | GetExecutionsSummary provides aggregated statistics about executions for specified entities. |
| `GetLatestExecutions` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/latest` | GetLatestExecutions retrieves the most recent execution for each specified entity. |
| `ListExecutions` *synq.entities.executions.v2.EntityExecutionsService* `GET /api/executions/v2/list` | ListExecutions retrieves a paginated list of executions for one or more entities. |
## Edit Integration Settings
`SCOPE_INTEGRATION_EDIT`
Create, update, and delete integrations and their connection settings.
| Method | Description |
| - | - |
| `AcceptPromptSuggestion` *synq.agent.sre.v1.SreAgentService* | AcceptPromptSuggestion applies a pending suggestion to the matching SRE agent |
| `DismissPromptSuggestion` *synq.agent.sre.v1.SreAgentService* | DismissPromptSuggestion marks a pending suggestion as dismissed with an optional |
| `RecordPromptSuggestion` *synq.agent.sre.v1.SreAgentService* | RecordPromptSuggestion is the producer-side endpoint used by the offline |
| `BatchGetIntegrations` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/by-id` | Fetch multiple integrations by id in one call. Ids not found (or not in the |
| `CreateIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1` | Create a new integration. The server assigns the id and returns the created |
| `DeleteIntegration` *synq.integrations.v1.IntegrationsService* `DELETE /api/integrations/v1/{integration_id}` | Delete an integration. The integration stops being scheduled and is removed |
| `DisableIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/{integration_id}/disable` | Disable an integration without deleting it. The configuration is kept but |
| `EnableIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/{integration_id}/enable` | Enable a previously disabled integration. No-op if already enabled. |
| `GetIntegration` *synq.integrations.v1.IntegrationsService* | Fetch a single integration by id. Secret config fields are masked. |
| `GetIntegrationHealth` *synq.integrations.v1.IntegrationsService* | Return current health plus a paginated history of recent runs for an |
| `ListIntegrations` *synq.integrations.v1.IntegrationsService* `GET /api/integrations/v1` | List all integrations in the workspace (optionally filtered by enabled |
| `RefreshIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/{integration_id}/refresh` | Trigger an immediate, ad-hoc refresh of an integration. Available only for |
| `UpdateIntegration` *synq.integrations.v1.IntegrationsService* `PUT /api/integrations/v1/{integration_id}` | Update an integration. The `config` is replaced wholesale — send the |
| `SetPlatformAuthentication` *synq.platforms.v1.PlatformsService* `POST /api/platforms/v1` | |
## Read Integration Settings
`SCOPE_INTEGRATION_READ`
View integrations and their connection settings.
| Method | Description |
| - | - |
| `GetConfig` *synq.agent.sre.v1.SreAgentService* | GetConfig returns the Scout configuration (system prompts + DWH connection |
| `ListPromptSuggestions` *synq.agent.sre.v1.SreAgentService* | ListPromptSuggestions returns prompt-improvement suggestions recorded for the |
| `Get` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `GET /api/extensions/atlan/integrations/v1` | |
| `BatchGetIntegrations` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/by-id` | Fetch multiple integrations by id in one call. Ids not found (or not in the |
| `GetIntegration` *synq.integrations.v1.IntegrationsService* | Fetch a single integration by id. Secret config fields are masked. |
| `GetIntegrationHealth` *synq.integrations.v1.IntegrationsService* | Return current health plus a paginated history of recent runs for an |
| `ListIntegrations` *synq.integrations.v1.IntegrationsService* `GET /api/integrations/v1` | List all integrations in the workspace (optionally filtered by enabled |
## Edit Users
`SCOPE_USER_EDIT`
Manage workspace members, including inviting users and updating their roles.
| Method | Description |
| - | - |
| `AddUserRole` *synq.users.v1.UsersService* `POST /api/users/v1/roles` | Grants a role to a user in the workspace. |
| `DeleteUserAliases` *synq.users.v1.UsersService* `POST /api/users/v1/aliases/delete` | Removes aliases from users. |
| `RemoveUserRole` *synq.users.v1.UsersService* `POST /api/users/v1/roles/remove` | Withdraws a role from a user in the workspace. |
| `UpsertUserAliases` *synq.users.v1.UsersService* `PUT /api/users/v1/aliases` | Links aliases to users. |
## Read Users
`SCOPE_USER_READ`
View workspace members and their roles.
| Method | Description |
| - | - |
| `ListDomainMembers` *synq.domains.members.v1.DomainMembersService* `GET /api/domains/members/v1/{domain_id}/members` | List domain members. |
| `ListUserDomains` *synq.domains.members.v1.DomainMembersService* `GET /api/domains/members/v1/users/{user_id}/domains` | List domains for the given user. |
| `SetDefaultUserDomain` *synq.domains.members.v1.DomainMembersService* `POST /api/domains/members/v1/{domain_id}/default-for-user` | Set default domain for user. |
| `BatchGetUsers` *synq.users.v1.UsersService* `POST /api/users/v1/batch-get` | |
| `GetByAlias` *synq.users.v1.UsersService* `POST /api/users/v1/by-alias` | |
| `GetByEmail` *synq.users.v1.UsersService* `GET /api/users/v1/by-email` | |
| `GetUserAliases` *synq.users.v1.UsersService* `GET /api/users/v1/aliases` | |
| `GetUserIdentities` *synq.users.v1.UsersService* `GET /api/users/v1/identities` | Returns all canonical identity strings for a user. |
| `ListUsers` *synq.users.v1.UsersService* `GET /api/users/v1` | |
## Assign Users
`SCOPE_USER_ASSIGN`
Assign users as owners of entities, issues, and other resources.
| Method | Description |
| - | - |
| `AddDomainMembers` *synq.domains.members.v1.DomainMembersService* `POST /api/domains/members/v1/{domain_id}/members` | Add domain members. |
| `RemoveDomainMembers` *synq.domains.members.v1.DomainMembersService* `POST /api/domains/members/v1/{domain_id}/members/remove` | Remove domain members. |
## Read Monitors
`SCOPE_MONITORS_READ`
View monitors and their configuration.
| Method | Description |
| - | - |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `ListConfigsMonitors` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `GET /api/monitors/custom-monitors/v1/by-config` | |
| `ListMonitors` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `GET /api/monitors/custom-monitors/v1` | |
| `History` *synq.monitors.history.v1.HistoryService* `GET /api/monitors/history/v1` | |
| `MonitorInfo` *synq.monitors.info.v1.MonitorInfoService* `GET /api/monitors/info/v1` | |
| `BatchGetLatestEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `POST /api/monitors/predictions/v1/entity-metrics:batchLatest` | Returns the most recent observed table-stats sample (row count, freshness) for each |
| `GetEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1/entity-metrics` | Returns raw metric timeseries for an entity. Identity-aware: resolves |
| `GetMonitorPredictions` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1` | Returns prediction timeseries for a monitor. The monitor must exist |
| `ListMonitorSegments` *synq.monitors.segments.v1.MonitorSegmentsService* `GET /api/monitors/segments/v1` | Lists the segments a monitor has ingested so far, with their raw value and |
## Edit Automatic Monitors
`SCOPE_MONITORS_AUTOMATIC_EDIT`
Enable, disable, and configure automatic monitors.
| Method | Description |
| - | - |
| `BatchDeleteDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `DELETE /api/monitors/automated/v1/deployment-rules/batch` | |
| `BatchDeployDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/batch/deployed` | |
| `BatchGetDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules/batch` | |
| `BatchUpdateDeploymentRuleTitle` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `PATCH /api/monitors/automated/v1/deployment-rules` | |
| `BatchUpsertDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/batch` | |
| `DeleteDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `DELETE /api/monitors/automated/v1/deployment-rule/{id}` | |
| `DeployDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/deployed` | |
| `GetDeployDeleteOverview` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rule/{id}/deleted` | |
| `GetDeployOverview` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rule` | |
| `ListDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules` | |
| `ListDeploymentRulesForAsset` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules/{asset_path}` | |
| `UpsertDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules` | |
## Edit Custom Monitors
`SCOPE_MONITORS_CUSTOM_EDIT`
Create, update, and delete custom monitors.
| Method | Description |
| - | - |
| `BatchCreateMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1` | |
| `BatchDeleteMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/delete` | |
| `BatchUpdateMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `PUT /api/monitors/custom-monitors/v1` | |
| `BatchUpdateMonitorMode` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/mode` | |
| `BatchUpdateMonitorSeverity` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/severity` | |
| `ConfigChangesOverview` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/config-changes` | |
| `BatchUpdateMonitorSegmentLabels` *synq.monitors.segments.v1.MonitorSegmentsService* `POST /api/monitors/segments/v1/labels` | Sets or clears the label of one or more segments. |
## Edit Custom SQL Monitors
`SCOPE_MONITORS_CUSTOM_SQL_EDIT`
Create and update custom monitors backed by a custom SQL query.
| Method | Description |
| - | - |
| `BatchCreateMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1` | |
| `BatchUpdateMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `PUT /api/monitors/custom-monitors/v1` | |
## Edit Alerts
`SCOPE_ALERTS_EDIT`
Create, update, and delete alert rules and notification channels.
| Method | Description |
| - | - |
| `Create` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1` | Create a new alert configuration. |
| `Delete` *synq.alerts.services.v1.AlertsService* `DELETE /api/alerts/v1` | Delete an existing alert configuration. |
| `ToggleEnabled` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1/toggle` | Enables or disables an existing alert configuration. |
| `Update` *synq.alerts.services.v1.AlertsService* `PUT /api/alerts/v1` | Update an existing alert configuration. |
| `Create` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2` | Create a new alert configuration. |
| `Delete` *synq.alerts.services.v2.AlertsService* `DELETE /api/alerts/v2` | Delete an existing alert configuration. |
| `Rename` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/rename` | Rename an existing alert configuration. |
| `SendTestAlert` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/test` | Delivers a static test message to the given targets so you can confirm that |
| `ToggleEnabled` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/toggle` | Enables or disables an existing alert configuration. |
| `ToggleIncidentOwnerAlert` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/incident-owner` | Turns the workspace's incident-owner alert on or off. This alert notifies |
| `UpdateSettings` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/settings` | Update the trigger and/or the kind-specific settings of an alert. |
| `UpdateTargets` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/targets` | Replace the delivery targets of an existing alert configuration. |
| `UpdateTemplate` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/template` | Set or clear the message template of an existing alert configuration. |
| `Upsert` *synq.alerts.services.v2.AlertsService* `PUT /api/alerts/v2` | Creates the alert when no alert holds `fqn`, otherwise replaces the stored |
## Read Alerts
`SCOPE_ALERTS_READ`
View alert rules and notification channels.
| Method | Description |
| - | - |
| `BatchGet` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1/get` | Get multiple alert configurations by their identifiers. |
| `List` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1/list` | Retrieves a list of alert configurations based on filters. |
| `BatchGet` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/get` | Get multiple alert configurations by their identifiers. |
| `GetIncidentOwnerAlert` *synq.alerts.services.v2.AlertsService* `GET /api/alerts/v2/incident-owner` | Reports whether the workspace's incident-owner alert is on. This alert has |
| `List` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/list` | Retrieves a list of alert configurations based on filters. |
## Edit Owners
`SCOPE_OWNERS_EDIT`
Create, update, and delete owners and their notification channels (Slack, email, Microsoft Teams, users).
| Method | Description |
| - | - |
| `DeleteOwner` *synq.owners.v1.OwnersService* `POST /api/owners/v1/delete` | Delete an owner and all of its ownerships. Idempotent — deleting an |
| `UpsertOwner` *synq.owners.v1.OwnersService* `POST /api/owners/v1` | Create or update an owner. The id is supplied by the caller (a UUID); |
## Read Owners
`SCOPE_OWNERS_READ`
View owners and their notification channels.
| Method | Description |
| - | - |
| `BatchGetEntityOverlays` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/by-id` | For each entity, list every overlay that claims it. |
| `BatchGetOverlayMemberCounts` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/member-counts` | Count the members of each named overlay. |
| `GetCoverageSummary` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage-summary` | Count coverage over one or more named candidate sets, without listing the |
| `ListEntityCoverage` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage` | List the candidate assets together with the overlays claiming each, and |
| `BatchGetOwners` *synq.owners.v1.OwnersService* `POST /api/owners/v1/batch-get` | Fetch multiple owners by id. Ids that do not exist (or, unless |
| `ListOwners` *synq.owners.v1.OwnersService* `GET /api/owners/v1` | List the owners in the workspace, paginated. |
## Edit Ownership
`SCOPE_OWNERSHIP_EDIT`
Create, update, and delete ownership assignments and the alerts they route to owners.
| Method | Description |
| - | - |
| `DeleteOwnership` *synq.owners.v1.OwnersService* `POST /api/owners/v1/ownerships/delete` | Delete an ownership by id. Idempotent — deleting an already-deleted or |
| `UpsertOwnership` *synq.owners.v1.OwnersService* `POST /api/owners/v1/{owner_id}/ownerships` | Create or update an ownership under an owner. The ownership id is supplied |
## Read Ownership
`SCOPE_OWNERSHIP_READ`
View ownership assignments and their alert routing.
| Method | Description |
| - | - |
| `BatchGetOwnerships` *synq.owners.v1.OwnersService* `POST /api/owners/v1/ownerships/batch-get` | Fetch multiple ownerships by id (across owners). Ids that do not exist (or, |
| `ListOwnerships` *synq.owners.v1.OwnersService* `GET /api/owners/v1/{owner_id}/ownerships` | List the ownerships of an owner, paginated. |
## Edit Data Products
`SCOPE_DATAPRODUCT_EDIT`
Create, update, and delete data products.
| Method | Description |
| - | - |
| `Delete` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/delete` | Delete a dataproduct by its identifier. |
| `RemoveDefinitionPart` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/definition/part/delete` | Remove definition part from the definition of an existing dataproduct. |
| `SetDefinition` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/definition` | Set the definition for an existing dataproduct. |
| `Upsert` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1` | Create or update a dataproduct. |
| `UpsertDefinitionPart` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/definition/part` | Upsert definition part to the definition of an existing dataproduct. |
| `Delete` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/delete` | Delete a data product. Idempotent — deleting an already-deleted or unknown |
| `RemoveDefinitionPart` *synq.dataproducts.v2.DataproductsService* `DELETE /api/dataproducts/v2/{id}/definition/parts/{part_id}` | Remove a single definition part by its part id. Idempotent. |
| `SetDefinition` *synq.dataproducts.v2.DataproductsService* `PUT /api/dataproducts/v2/{id}/definition` | Replace the entire membership definition of a data product. |
| `Upsert` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2` | Create or update a data product. The id is supplied by the caller (a UUID); |
| `UpsertDefinitionPart` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/{id}/definition/parts` | Add or replace a single definition part (matched by its part id). |
## Read Data Products
`SCOPE_DATAPRODUCT_READ`
View data products and their contents.
| Method | Description |
| - | - |
| `Get` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/get` | Get multiple dataproducts by their identifiers. |
| `List` *synq.dataproducts.v1.DataproductsService* `GET /api/dataproducts/v1` | List all the dataproducts in the current scope. |
| `BatchGet` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/batch-get` | Fetch multiple data products by id. Ids that do not exist (or, unless |
| `List` *synq.dataproducts.v2.DataproductsService* `GET /api/dataproducts/v2` | List the data products in the workspace, paginated. Narrow with `folder` |
| `ListMembers` *synq.dataproducts.v2.DataproductsService* `GET /api/dataproducts/v2/{id}/members` | List the assets that currently belong to a data product, resolved from its |
| `BatchGetEntityOverlays` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/by-id` | For each entity, list every overlay that claims it. |
| `BatchGetOverlayMemberCounts` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/member-counts` | Count the members of each named overlay. |
| `GetCoverageSummary` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage-summary` | Count coverage over one or more named candidate sets, without listing the |
| `ListEntityCoverage` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage` | List the candidate assets together with the overlays claiming each, and |
## Edit Domains
`SCOPE_DOMAIN_EDIT`
Create, update, and delete domains.
| Method | Description |
| - | - |
| `Delete` *synq.domains.v1.DomainsService* `POST /api/domains/v1/delete` | Delete a domain by its identifier. |
| `RemoveDefinitionPart` *synq.domains.v1.DomainsService* `DELETE /api/domains/v1/{domain_id}/definition/parts/{part_id}` | Remove definition part from the definition of an existing domain. |
| `SetDefinition` *synq.domains.v1.DomainsService* `PUT /api/domains/v1/{domain_id}/definition` | Set the definition for an existing domain. |
| `Upsert` *synq.domains.v1.DomainsService* `POST /api/domains/v1` | Create or update a domain. |
| `UpsertDefinitionPart` *synq.domains.v1.DomainsService* `POST /api/domains/v1/{domain_id}/definition/parts` | Upsert definition part to the definition of an existing domain. |
| `Delete` *synq.domains.v2.DomainsService* `POST /api/domains/v2/delete` | Delete a domain. Idempotent — deleting an already-deleted or unknown domain |
| `RemoveDefinitionPart` *synq.domains.v2.DomainsService* `DELETE /api/domains/v2/{id}/definition/parts/{part_id}` | Remove a single definition part by its part id. Idempotent. |
| `SetDefinition` *synq.domains.v2.DomainsService* `PUT /api/domains/v2/{id}/definition` | Replace the entire membership definition of a domain. |
| `Upsert` *synq.domains.v2.DomainsService* `POST /api/domains/v2` | Create or update a domain. The id is supplied by the caller (a UUID); |
| `UpsertDefinitionPart` *synq.domains.v2.DomainsService* `POST /api/domains/v2/{id}/definition/parts` | Add or replace a single definition part (matched by its part id). |
## Read Domains
`SCOPE_DOMAIN_READ`
View domains and their contents.
| Method | Description |
| - | - |
| `BatchGet` *synq.domains.v1.DomainsService* `POST /api/domains/v1/batch-get` | Get multiple domains by their identifiers. |
| `FetchDomainWithSubDomains` *synq.domains.v1.DomainsService* `GET /api/domains/v1/{domain_id}/sub-domains` | Get a domain with its sub-domains |
| `List` *synq.domains.v1.DomainsService* `GET /api/domains/v1` | List all the domains in the current workspace. |
| `BatchGet` *synq.domains.v2.DomainsService* `POST /api/domains/v2/batch-get` | Fetch multiple domains by id. Ids that do not exist (or, unless |
| `FetchDomainWithSubDomains` *synq.domains.v2.DomainsService* `GET /api/domains/v2/{id}/sub-domains` | Fetch a domain together with its direct sub-domains, keyed by id. |
| `List` *synq.domains.v2.DomainsService* `GET /api/domains/v2` | List the domains in the workspace, paginated. Narrow to one level of the |
| `ListMembers` *synq.domains.v2.DomainsService* `GET /api/domains/v2/{id}/members` | List the assets that currently belong to a domain, resolved from its |
## Manage Extensions
`SCOPE_EXTENSIONS_MANAGE`
Manage platform extensions, including data warehouse agent and catalog connections.
| Method | Description |
| - | - |
| `GenerateConnectionsYaml` *synq.agent.dwh.v1.ConnectionsService* | GenerateConnectionsYaml converts data warehouse connections to YAML configuration format. |
| `ListConnections` *synq.agent.dwh.v1.ConnectionsService* | ListConnections retrieves data warehouse connections for a workspace. |
| `Activate` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `PATCH /api/extensions/atlan/integrations/v1` | Activate or deactivate the integration. |
| `Ping` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `POST /api/extensions/atlan/integrations/v1/ping` | Pings Atlan with the given connection credentials. |
| `Remove` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `DELETE /api/extensions/atlan/integrations/v1` | |
| `Upsert` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `POST /api/extensions/atlan/integrations/v1` | Upserts an integration and validates it by checking if the provided API URL and token are valid. |
| `GetAtlanConnections` *synq.extensions.atlan.provider.v1.AtlanProviderService* `GET /api/extensions/atlan/provider/v1/connections` | |
| `GetAtlanDataProducts` *synq.extensions.atlan.provider.v1.AtlanProviderService* `GET /api/extensions/atlan/provider/v1/data-products` | |
| `GetAtlanDomains` *synq.extensions.atlan.provider.v1.AtlanProviderService* `GET /api/extensions/atlan/provider/v1/domains` | |
| `FetchRuns` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/runs` | |
| `GetConnectionMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/connection-mappings` | |
| `GetDomainMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/domain-mappings` | |
| `GetMappingToggles` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/mappings/toggle` | |
| `GetProductMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/product-mappings` | |
| `RemoveConnectionMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `DELETE /api/extensions/atlan/workflows/v1/connection-mappings` | |
| `SetConnectionMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `POST /api/extensions/atlan/workflows/v1/connection-mappings` | |
| `SetMappingToggles` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `POST /api/extensions/atlan/workflows/v1/mappings/toggle` | |
| `Synchronize` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `POST /api/extensions/atlan/workflows/v1/synchronize` | Run a synchronization job for the integration. |
## Read Usage
`SCOPE_USAGE_GET`
View workspace usage and quota metrics.
| Method | Description |
| - | - |
| `GetUsage` *synq.domains.v1.DomainsService* `GET /api/domains/v1/usage` | Report how many domains the workspace uses, split into top-level and sub-domains. |
| `GetUsage` *synq.domains.v2.DomainsService* `GET /api/domains/v2/usage` | Report how many domains the workspace uses, split into top-level and sub-domains. |
## MCP Read Access
`SCOPE_MCP_READ`
Use any read-only MCP (Model Context Protocol) tool, including tools added in the future.
| Method | Description |
| - | - |
| `GenerateConnectionsYaml` *synq.agent.dwh.v1.ConnectionsService* | GenerateConnectionsYaml converts data warehouse connections to YAML configuration format. |
| `ListConnections` *synq.agent.dwh.v1.ConnectionsService* | ListConnections retrieves data warehouse connections for a workspace. |
| `GetAuditLog` *synq.agent.recon.v1.AuditLogService* | GetAuditLog returns a single audit log by invocation ID. |
| `ListAuditLogs` *synq.agent.recon.v1.AuditLogService* | ListAuditLogs returns audit logs ordered from newest to oldest, optionally filtered by suite and tim... |
| `BatchReconciliationRunState` *synq.agent.recon.v1.RunStateService* | BatchReconciliationRunState returns the latest concluded run and any ongoing |
| `ListReconciliationRunState` *synq.agent.recon.v1.RunStateService* | ListReconciliationRunState returns runs filtered by status and/or suite\_id. |
| `GetSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | Get the latest version of a suite config by suite\_id. |
| `GetSuiteConfigVersion` *synq.agent.recon.v1.SuiteConfigService* | Get a specific historical version of a suite config. |
| `ListSuiteConfigVersions` *synq.agent.recon.v1.SuiteConfigService* | Get version history for a suite config. |
| `ListSuiteConfigs` *synq.agent.recon.v1.SuiteConfigService* | List latest suite configs with filtering and pagination. |
| `GetSuiteDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | Fetch a deployment by suite\_id or deployment\_id. |
| `ListSuiteDeploymentHistory` *synq.agent.recon.v1.SuiteDeploymentService* | List the append-only history of a deployment (every promote, |
| `ListSuiteDeployments` *synq.agent.recon.v1.SuiteDeploymentService* | List deployments for the caller's workspace. Filters support answering |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | SubmitFeatureRequest submits a feature request to the Coalesce Quality product team. |
| `GetConfig` *synq.agent.sre.v1.SreAgentService* | GetConfig returns the Scout configuration (system prompts + DWH connection |
| `ListPromptSuggestions` *synq.agent.sre.v1.SreAgentService* | ListPromptSuggestions returns prompt-improvement suggestions recorded for the |
| `BatchSuggestionStates` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `GetActiveSuggestionStatesFor` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `ListSuggestionStates` *synq.agent.sre.v1.TestsSuggestionStateService* | |
| `BatchLatestConclusions` *synq.agent.sre.v1.TriageService* `GET /api/triage/v1/conclusions` | Retrieves the latest triage state for one or more issues. This method is used by the LLM agent |
| `BatchTriageState` *synq.agent.sre.v1.TriageService* `GET /api/triage/v1/states` | Retrieves the triage state for one or more issues. This method is used by the LLM agent |
| `ListTriageState` *synq.agent.sre.v1.TriageService* `GET /api/triage/v1/statuses` | Retrieves the triage state for one or more issues. This method is used by the LLM agent |
| `BatchGet` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/get` | Get multiple alert configurations by their identifiers. |
| `GetIncidentOwnerAlert` *synq.alerts.services.v2.AlertsService* `GET /api/alerts/v2/incident-owner` | Reports whether the workspace's incident-owner alert is on. This alert has |
| `List` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/list` | Retrieves a list of alert configurations based on filters. |
| `BatchGetSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/by-id` | |
| `GetSqlTestDeployDeleteOverview` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1/{id}/deleted` | Preview the effect of deleting a rule, without deleting it. |
| `GetSqlTestDeployOverview` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1/overview` | Preview the effect of deploying a rule (which have not yet been saved), without |
| `ListSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1` | |
| `BatchGetSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `POST /api/datachecks/sqltests/v1/by-id` | Get SqlTests by their IDs. |
| `ListSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `GET /api/datachecks/sqltests/v1` | List SqlTests for given annotations. |
| `BatchGetTestSuggestionsForEntities` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `GET /api/datachecks/testsuggestions/v1` | |
| `Get` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/get` | Get multiple dataproducts by their identifiers. |
| `List` *synq.dataproducts.v1.DataproductsService* `GET /api/dataproducts/v1` | List all the dataproducts in the current scope. |
| `BatchGet` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/batch-get` | Fetch multiple data products by id. Ids that do not exist (or, unless |
| `List` *synq.dataproducts.v2.DataproductsService* `GET /api/dataproducts/v2` | List the data products in the workspace, paginated. Narrow with `folder` |
| `ListMembers` *synq.dataproducts.v2.DataproductsService* `GET /api/dataproducts/v2/{id}/members` | List the assets that currently belong to a data product, resolved from its |
| `BatchGet` *synq.domains.v2.DomainsService* `POST /api/domains/v2/batch-get` | Fetch multiple domains by id. Ids that do not exist (or, unless |
| `FetchDomainWithSubDomains` *synq.domains.v2.DomainsService* `GET /api/domains/v2/{id}/sub-domains` | Fetch a domain together with its direct sub-domains, keyed by id. |
| `List` *synq.domains.v2.DomainsService* `GET /api/domains/v2` | List the domains in the workspace, paginated. Narrow to one level of the |
| `ListMembers` *synq.domains.v2.DomainsService* `GET /api/domains/v2/{id}/members` | List the assets that currently belong to a domain, resolved from its |
| `BatchGetEntityUsage` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/usage` | Read how each asked entity is used over the window, in one line: what loads it, what runs and |
| `BatchGetPopularity` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/popularity` | Read how popular each asked entity is over the window: its counted reads and writes per measure, |
| `GetActivityOverview` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/overview` | Read a workspace's queries and their cost per group of logins, per platform, and its most read |
| `GetActorSummary` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-summary` | Read what one actor read, built and wrote over the window, what its queries cost per compute and |
| `GetEntityActivitySeries` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/series` | Read how access to one entity moves over the window: per direction, a point per UTC hour, a point |
| `ListAccessSignals` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/signals` | List the entities of the workspace, or of a selection, that one signal names, such as the tables |
| `ListActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-list` | List the workspace's actors, the service accounts, tools, people, logins nothing classifies and |
| `ListEntityActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actors` | Read who read and wrote each asked entity over the window: the jobs, the service accounts, the |
| `ListPersonActivity` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/person` | Read what one person's logins read and wrote over the window, per entity, direction and measure. |
| `BatchGetEntityAnnotations` *synq.entities.annotations.v1.AnnotationsService* `POST /api/entities/annotations/v1/batchGet` | Read the annotations carried by specific entities. |
| `ListAnnotations` *synq.entities.annotations.v1.AnnotationsService* `GET /api/entities/annotations/v1` | List all available annotations with their usage counts across entities. |
| `BatchChange` *synq.entities.changes.v1.ChangesService* `POST /api/changes/v1/by-id` | BatchChange retrieves multiple entity changes by their IDs in a single request. |
| `List` *synq.entities.changes.v1.ChangesService* `GET /api/changes/v1` | Retrieves the complete change history for specified entities. |
| `GetCoverage` *synq.entities.checks.v1.ChecksCoverageService* | GetCoverage retrieves the coverage of checks for a specific entity. |
| `BatchGetCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/batch-get` | Read the categories of specific checks. Ids that do not exist are omitted |
| `ListCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/vocabulary` | List the workspace's category vocabulary: the category names in use on |
| `ListCategorisationFilters` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/filters` | List the attribute values the workspace's checks actually have — the |
| `ListCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/list` | List the workspace's checks with their resolved categories. Returns every |
| `BatchGetRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/batch-get` | Read specific rules by id — typically to resolve the `rule_id` a |
| `GetRecategorisationStatus` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/recategorisation-status` | Progress of the re-categorisation sweep a rule change schedules. The sweep |
| `ListRuleMatches` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/matches` | Report how many checks each rule currently categorises, with a sample of |
| `ListRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/list` | List the rules that apply to the caller's workspace — its own rules plus |
| `PreviewRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/preview` | Report what a candidate rule would change, without saving it. |
| `ListCode` *synq.entities.code.v1.CodeService* `GET /api/code/v1` | ListCode retrieves all code artifacts associated with a specific entity. |
| `GetTableConstraints` *synq.entities.constraints.v1.TableConstraintsService* `GET /api/constraints/v1/table-constraints` | Get table constraints (primary keys, foreign keys, indexes, etc.) for a given entity. |
| `BatchDatabaseCoordinates` *synq.entities.coordinates.v1.DatabaseCoordinatesService* `POST /api/coordinates/v1/database/by-id` | Get multiple DatabaseCoordinates for identifiers. |
| `BatchIdsByCoordinates` *synq.entities.coordinates.v1.DatabaseCoordinatesService* `POST /api/coordinates/v1/database/by-fqn` | Given database FQN return possible entity ids. |
| `ListCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `POST /api/entities/custom/v1/check-relationships/list` | List every relationship that attaches a check to the entities it |
| `ListEntities` *synq.entities.custom.v1.EntitiesService* `GET /api/entities/custom/v1/entities` | List all entities. |
| `ListEntityFeatures` *synq.entities.custom.v1.FeaturesService* `POST /api/entities/custom/v1/features/list` | List the features of one entity, or of every custom entity in the |
| `ListEntityGroups` *synq.entities.custom.v1.GroupsService* `POST /api/entities/custom/v1/groups/list` | ListEntityGroups lists all groups. |
| `ListRelationships` *synq.entities.custom.v1.RelationshipsService* `POST /api/entities/custom/v1/relationships/list` | List relationships touching the given entities, or every relationship in |
| `BatchGetEntities` *synq.entities.entities.v1.EntitiesService* `POST /api/entities/v1/by-id` | Get multiple entities by their identifiers. |
| `GetEntity` *synq.entities.entities.v1.EntitiesService* | Get specific entity by its identifier. |
| `ListEntities` *synq.entities.entities.v1.EntitiesService* `GET /api/entities/v1` | List entities matching the given filter |
| `SearchEntities` *synq.entities.entities.v1.EntitiesService* `GET /api/entities/v1/search` | Search for entities. |
| `BatchGetExecutions` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/by-id` | BatchGetExecutions retrieves multiple executions by their IDs. |
| `GetExecutionsSummary` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/summary` | GetExecutionsSummary provides aggregated statistics about executions for specified entities. |
| `GetLatestExecutions` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/latest` | GetLatestExecutions retrieves the most recent execution for each specified entity. |
| `ListExecutions` *synq.entities.executions.v2.EntityExecutionsService* `GET /api/executions/v2/list` | ListExecutions retrieves a paginated list of executions for one or more entities. |
| `Browse` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/browse` `GET /api/entities/folders/v1/browse` | Browse the hierarchy one level at a time. With no `parent`, returns the |
| `GetFolderOf` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/folder-of` `GET /api/entities/folders/v1/folder-of` | Given entity ids, return the folder breadcrumb(s) each entity lives in — |
| `ListMembers` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/members` `GET /api/entities/folders/v1/members` | List the entity ids contained in a folder, paginated. Direct members only by |
| `GetImpact` *synq.entities.impact.v1.ImpactService* `POST /api/impact/v1` | |
| `BatchGetFoldNodes` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/nodes/batch-get` | Read fold nodes by entity id: which fold a node in a lineage response |
| `BatchGetFolds` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/batch-get` | Read folds by id. An id with no fold is absent from the response. |
| `ListFolds` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `GET /api/lineage/folds/v1alpha` | List the ids of every fold in the workspace, switched off ones included, |
| `PreviewFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/preview` | Show what a matcher would fold, before it is saved: how many entities and |
| `GetLineage` *synq.entities.lineage.v1.LineageService* `POST /api/lineage/v1` | |
| `BatchOrchestration` *synq.entities.orchestration.v1.OrchestrationService* `GET /api/orchestration/v1` | BatchOrchestration returns, for each requested entity, the jobs, job steps and tasks that ran it |
| `BatchGetPlatformUsers` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/batch-get` | Read logins by platform user id. Ids no integration declared and with no activity in the window |
| `ListLinkSuggestions` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/link-suggestions` | Suggest which workspace member each unclassified login may belong to, by matching the login |
| `ListPlatformUsers` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/list` | List the workspace's logins by their share of the workspace's activity, highest first. The first |
| `BatchGetServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/batch-get` | Read service accounts by id, built-in ids included. Ids that do not exist are left out. |
| `ListServiceAccountSuggestions` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/suggestions` | Suggest service accounts for the logins the evidence says are not a person, each as a change set |
| `ListServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/list` | List every workspace service account, then every built-in one, in the order they apply, and the |
| `BatchResolveIdentifiers` *synq.entities.resolve.v1.IdentifierResolveService* `POST /api/resolve/v1/by-id` | Resolve multiple identifiers, returning their Coalesce Quality paths and identity groups. |
| `BatchSchemaMismatches` *synq.entities.schemas.v1.SchemaMismatchesService* `POST /api/schema-mismatches/v1` | Get schema mismatches for a batch of entities by their identifiers. |
| `GetSchema` *synq.entities.schemas.v1.SchemasService* `GET /api/schema/v1` | Get schema of the entity its identifier. |
| `BatchSqlDetails` *synq.entities.sql\_insights.v1.SqlInsightsService* `POST /api/sql-insights/v1/details:batch` | BatchSqlDetails returns the SQL details for each of the requested entities, |
| `ListJoinPatterns` *synq.entities.sql\_insights.v1.SqlInsightsService* `GET /api/sql-insights/v1/join-patterns` | ListJoinPatterns lists how other SQL joins a table, or one of its columns: |
| `ListSqlConstructs` *synq.entities.sql\_insights.v1.SqlInsightsService* `GET /api/sql-insights/v1/constructs` | ListSqlConstructs returns the distribution of SQL constructs across all |
| `BatchGetIncidents` *synq.entities.status.v1.EntityIncidentsService* `POST /api/status/v1/incidents/by-id` | Get multiple entity statuses by their identifiers. |
| `GetIncidents` *synq.entities.status.v1.EntityIncidentsService* | Get specific entity status by its identifier. |
| `BatchGetIssuesStatus` *synq.entities.status.v1.EntityIssuesService* `POST /api/status/v1/issues/by-id` | Get multiple entity statuses by their identifiers. |
| `GetDiffByChangeId` *synq.git.commits.v1.CommitsService* `GET /api/git/v1/change/{change_id}` | |
| `GetDiffByCommit` *synq.git.commits.v1.CommitsService* `GET /api/git/v1/commit/{commit_sha}` | |
| `BatchGetIncidents` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/by-id` | |
| `ListIncidents` *synq.incidents.v1.IncidentsService* `GET /api/incidents/v1` | |
| `BatchGetIssues` *synq.issues.issues.v1.IssuesService* `POST /api/issues/v1/by-id` | |
| `ListIssues` *synq.issues.issues.v1.IssuesService* `GET /api/issues/v1` | |
| `BatchGetIssues` *synq.issues.v2.IssuesService* `POST /api/issues/v2/by-id` | |
| `ListIssues` *synq.issues.v2.IssuesService* `GET /api/issues/v2` | |
| `BatchGetEntityKnowledge` *synq.knowledge.v1alpha.KnowledgeService* `POST /api/knowledge/v1alpha/entities/by-id` | Read the knowledge held about entities: the description people wrote for |
| `BatchGetKnowledgePages` *synq.knowledge.v1alpha.KnowledgeService* `POST /api/knowledge/v1alpha/pages/by-id` | Read knowledge pages by id: their content, where they sit in the tree, |
| `ListKnowledgePageLinks` *synq.knowledge.v1alpha.KnowledgeService* `GET /api/knowledge/v1alpha/pages/{page_id}/links` | List what is pinned to a knowledge page, in either direction: the ids of |
| `ListKnowledgePages` *synq.knowledge.v1alpha.KnowledgeService* `GET /api/knowledge/v1alpha/pages` | List the ids of knowledge pages, ordered by name, optionally narrowed by a |
| `GetDeployDeleteOverview` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rule/{id}/deleted` | |
| `GetDeployOverview` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rule` | |
| `ListDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules` | |
| `ListDeploymentRulesForAsset` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules/{asset_path}` | |
| `History` *synq.monitors.history.v1.HistoryService* `GET /api/monitors/history/v1` | |
| `MonitorInfo` *synq.monitors.info.v1.MonitorInfoService* `GET /api/monitors/info/v1` | |
| `BatchGetLatestEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `POST /api/monitors/predictions/v1/entity-metrics:batchLatest` | Returns the most recent observed table-stats sample (row count, freshness) for each |
| `GetEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1/entity-metrics` | Returns raw metric timeseries for an entity. Identity-aware: resolves |
| `GetMonitorPredictions` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1` | Returns prediction timeseries for a monitor. The monitor must exist |
| `ListMonitorSegments` *synq.monitors.segments.v1.MonitorSegmentsService* `GET /api/monitors/segments/v1` | Lists the segments a monitor has ingested so far, with their raw value and |
| `BatchGetEntityOverlays` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/by-id` | For each entity, list every overlay that claims it. |
| `BatchGetOverlayMemberCounts` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/member-counts` | Count the members of each named overlay. |
| `GetCoverageSummary` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage-summary` | Count coverage over one or more named candidate sets, without listing the |
| `ListEntityCoverage` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage` | List the candidate assets together with the overlays claiming each, and |
| `BatchGetOwners` *synq.owners.v1.OwnersService* `POST /api/owners/v1/batch-get` | Fetch multiple owners by id. Ids that do not exist (or, unless |
| `BatchGetOwnerships` *synq.owners.v1.OwnersService* `POST /api/owners/v1/ownerships/batch-get` | Fetch multiple ownerships by id (across owners). Ids that do not exist (or, |
| `ListOwners` *synq.owners.v1.OwnersService* `GET /api/owners/v1` | List the owners in the workspace, paginated. |
| `ListOwnerships` *synq.owners.v1.OwnersService* `GET /api/owners/v1/{owner_id}/ownerships` | List the ownerships of an owner, paginated. |
| `ResolveQuery` *synq.queries.v1.QueriesService* `POST /api/queries/v1/resolve` | Evaluate a selection and return the matched entity ids, paginated. Pass a |
| `TranslateResolverQl` *synq.queries.v1.QueriesService* `POST /api/queries/v1/translate-resolver-ql` | Compile a ResolverQL expression into a structured public `Query`. Unlike |
| `BatchGet` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/batch-get` | Fetch multiple saved views by id. Ids the caller cannot see, or that do not |
| `List` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/list` | List the saved views the calling user can see. By default returns every |
| `SetPin` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/set-pin` | Pin or unpin a saved view to the calling user's main screen. Pinning is a |
| `GetByEmail` *synq.users.v1.UsersService* `GET /api/users/v1/by-email` | |
| `GetUserIdentities` *synq.users.v1.UsersService* `GET /api/users/v1/identities` | Returns all canonical identity strings for a user. |
| `ListUsers` *synq.users.v1.UsersService* `GET /api/users/v1` | |
## MCP Write Access
`SCOPE_MCP_WRITE`
Use any write MCP (Model Context Protocol) tool, including tools added in the future.
| Method | Description |
| - | - |
| `Create` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2` | Create a new alert configuration. |
| `Delete` *synq.alerts.services.v2.AlertsService* `DELETE /api/alerts/v2` | Delete an existing alert configuration. |
| `Rename` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/rename` | Rename an existing alert configuration. |
| `SendTestAlert` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/test` | Delivers a static test message to the given targets so you can confirm that |
| `ToggleEnabled` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/toggle` | Enables or disables an existing alert configuration. |
| `ToggleIncidentOwnerAlert` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/incident-owner` | Turns the workspace's incident-owner alert on or off. This alert notifies |
| `UpdateSettings` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/settings` | Update the trigger and/or the kind-specific settings of an alert. |
| `UpdateTargets` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/targets` | Replace the delivery targets of an existing alert configuration. |
| `Upsert` *synq.alerts.services.v2.AlertsService* `PUT /api/alerts/v2` | Creates the alert when no alert holds `fqn`, otherwise replaces the stored |
| `DeleteSqlTestDeploymentRule` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `DELETE /api/datachecks/sql-test-deployment-rules/v1/{id}` | |
| `DeploySqlTestDeploymentRule` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/deployed` | Deploy (create or update) a SqlTestDeploymentRule and immediately sync tests |
| `BatchCreateTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `POST /api/datachecks/testsuggestions/v1` | |
| `Delete` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/delete` | Delete a data product. Idempotent — deleting an already-deleted or unknown |
| `Upsert` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2` | Create or update a data product. The id is supplied by the caller (a UUID); |
| `Delete` *synq.domains.v2.DomainsService* `POST /api/domains/v2/delete` | Delete a domain. Idempotent — deleting an already-deleted or unknown domain |
| `Upsert` *synq.domains.v2.DomainsService* `POST /api/domains/v2` | Create or update a domain. The id is supplied by the caller (a UUID); |
| `DeleteCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/delete` | Clear the explicit categories of one or more checks, on both dimensions. |
| `UpsertCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories` | Set the category of one or more checks explicitly. An explicit category is |
| `DeleteRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/delete` | Delete a categorisation rule. Checks the rule categorised fall back to the |
| `UpsertRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules` | Create or update a categorisation rule. |
| `ApplyServiceAccountChanges` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/apply` | Write a planned change set, all of it or nothing. It is refused with FAILED\_PRECONDITION when the |
| `PlanServiceAccountChanges` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/plan` | Report what a change set would change, without writing it: every login slice whose |
| `CreateIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1` | |
| `DeleteComment` *synq.incidents.v1.IncidentsService* `DELETE /api/incidents/v1/{incident_id}/comment/{comment_id}` | Delete a comment on an incident. Only the actor who posted the comment may |
| `PostComment` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/comment` | Post a comment on an incident. |
| `DeleteComment` *synq.issues.v2.IssuesService* `DELETE /api/issues/v2/{issue_id}/comment/{comment_id}` | Delete a comment on an issue. Only the actor who posted the comment may |
| `PostComment` *synq.issues.v2.IssuesService* `POST /api/issues/v2/{issue_id}/comment` | Post a comment on an issue. |
| `DeleteDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `DELETE /api/monitors/automated/v1/deployment-rule/{id}` | |
| `DeployDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/deployed` | |
| `BatchUpdateMonitorSegmentLabels` *synq.monitors.segments.v1.MonitorSegmentsService* `POST /api/monitors/segments/v1/labels` | Sets or clears the label of one or more segments. |
| `DeleteOwner` *synq.owners.v1.OwnersService* `POST /api/owners/v1/delete` | Delete an owner and all of its ownerships. Idempotent — deleting an |
| `DeleteOwnership` *synq.owners.v1.OwnersService* `POST /api/owners/v1/ownerships/delete` | Delete an ownership by id. Idempotent — deleting an already-deleted or |
| `UpsertOwner` *synq.owners.v1.OwnersService* `POST /api/owners/v1` | Create or update an owner. The id is supplied by the caller (a UUID); |
| `UpsertOwnership` *synq.owners.v1.OwnersService* `POST /api/owners/v1/{owner_id}/ownerships` | Create or update an ownership under an owner. The ownership id is supplied |
| `Delete` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/delete` | Delete a saved view. Idempotent — deleting an already-deleted or unknown |
| `Upsert` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1` | Create or update a saved view. |
## Read Reconciliation Audits and Settings
`SCOPE_RECON_READ`
View reconciliation suites, settings, and audit results.
| Method | Description |
| - | - |
| `GetAuditLog` *synq.agent.recon.v1.AuditLogService* | GetAuditLog returns a single audit log by invocation ID. |
| `ListAuditLogs` *synq.agent.recon.v1.AuditLogService* | ListAuditLogs returns audit logs ordered from newest to oldest, optionally filtered by suite and tim... |
| `BatchReconciliationRunState` *synq.agent.recon.v1.RunStateService* | BatchReconciliationRunState returns the latest concluded run and any ongoing |
| `ListReconciliationRunState` *synq.agent.recon.v1.RunStateService* | ListReconciliationRunState returns runs filtered by status and/or suite\_id. |
| `GetSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | Get the latest version of a suite config by suite\_id. |
| `GetSuiteConfigVersion` *synq.agent.recon.v1.SuiteConfigService* | Get a specific historical version of a suite config. |
| `ListSuiteConfigVersions` *synq.agent.recon.v1.SuiteConfigService* | Get version history for a suite config. |
| `ListSuiteConfigs` *synq.agent.recon.v1.SuiteConfigService* | List latest suite configs with filtering and pagination. |
| `GetSuiteDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | Fetch a deployment by suite\_id or deployment\_id. |
| `ListSuiteDeploymentHistory` *synq.agent.recon.v1.SuiteDeploymentService* | List the append-only history of a deployment (every promote, |
| `ListSuiteDeployments` *synq.agent.recon.v1.SuiteDeploymentService* | List deployments for the caller's workspace. Filters support answering |
## Edit Reconciliation Settings
`SCOPE_RECON_EDIT`
Create and update reconciliation suites and their settings.
| Method | Description |
| - | - |
| `IngestAuditLog` *synq.agent.recon.v1.AuditLogService* | IngestAuditLog stores a reconciliation run audit log. |
| `CancelReconciliationRun` *synq.agent.recon.v1.RunStateService* | CancelReconciliationRun marks a SCHEDULED or IN\_PROGRESS run as |
| `DeleteSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | Delete a managed suite config (soft delete — inserts a deleted version). |
| `SubmitAdhocSuite` *synq.agent.recon.v1.SuiteConfigService* | Submit an ad-hoc suite for one-off execution. |
| `UpsertSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | Upsert a managed suite config. Creates if new, updates with new version if exists. |
## Promote Reconciliation Suites to run on Schedule or API
`SCOPE_RECON_PROMOTE`
Promote reconciliation suites so they run on a schedule or via the API.
| Method | Description |
| - | - |
| `PauseDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | Pause a deployment. Scheduled and API-triggered runs are suppressed until |
| `PromoteSuite` *synq.agent.recon.v1.SuiteDeploymentService* | Promote a suite config into production. Freezes a snapshot of the config |
| `ResumeDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | Resume a paused deployment. |
| `TriggerDeployment` *synq.agent.recon.v1.SuiteDeploymentService* `POST /api/recon/v1/deployments/trigger` | Trigger an on-demand execution of a promoted deployment. Enqueues a run |
| `UnpromoteSuite` *synq.agent.recon.v1.SuiteDeploymentService* | Unpromote a deployment. Marks it inactive; scheduler and API triggers |
| `UpdateDeploymentAnnotations` *synq.agent.recon.v1.SuiteDeploymentService* | Replace the deployment-level annotation overlay on an existing |
| `UpdateDeploymentCaseImpacts` *synq.agent.recon.v1.SuiteDeploymentService* | Patch the per-case ReconciliationFailureImpact map on an existing deployment |
| `UpdateDeploymentCategories` *synq.agent.recon.v1.SuiteDeploymentService* | Change the categories declared for a deployment's reconciliation checks |
| `UpdateDeploymentMappings` *synq.agent.recon.v1.SuiteDeploymentService* | Rewire connection → integration mappings on an existing deployment |
| `UpdateDeploymentSchedule` *synq.agent.recon.v1.SuiteDeploymentService* | Change schedule and/or triggerable\_by\_api on an existing deployment |
## Edit Check Categorisation Rules
`SCOPE_POLICIES_EDIT`
Create and update rules that categorise data quality checks.
| Method | Description |
| - | - |
| `DeleteCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `POST /api/entities/checks/v1/categories:delete` | DeleteCheckCategories clears the explicit category (both dimensions) |
| `ListCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `GET /api/entities/checks/v1/categories` | ListCheckCategories returns every explicit check category in the |
| `UpsertCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `POST /api/entities/checks/v1/categories:upsert` | UpsertCheckCategories sets (or replaces) the explicit category for |
| `BatchGetCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/batch-get` | Read the categories of specific checks. Ids that do not exist are omitted |
| `DeleteCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/delete` | Clear the explicit categories of one or more checks, on both dimensions. |
| `ListCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/vocabulary` | List the workspace's category vocabulary: the category names in use on |
| `ListCategorisationFilters` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/filters` | List the attribute values the workspace's checks actually have — the |
| `ListCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/list` | List the workspace's checks with their resolved categories. Returns every |
| `UpsertCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories` | Set the category of one or more checks explicitly. An explicit category is |
| `BatchGetRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/batch-get` | Read specific rules by id — typically to resolve the `rule_id` a |
| `DeleteRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/delete` | Delete a categorisation rule. Checks the rule categorised fall back to the |
| `GetRecategorisationStatus` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/recategorisation-status` | Progress of the re-categorisation sweep a rule change schedules. The sweep |
| `ListRuleMatches` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/matches` | Report how many checks each rule currently categorises, with a sample of |
| `ListRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/list` | List the rules that apply to the caller's workspace — its own rules plus |
| `PreviewRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/preview` | Report what a candidate rule would change, without saving it. |
| `UpsertRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules` | Create or update a categorisation rule. |
## Read Check Categorisation Rules and Status
`SCOPE_POLICIES_READ`
View check categorisation rules and their status.
| Method | Description |
| - | - |
| `ListCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `GET /api/entities/checks/v1/categories` | ListCheckCategories returns every explicit check category in the |
| `BatchGetCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/batch-get` | Read the categories of specific checks. Ids that do not exist are omitted |
| `ListCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/vocabulary` | List the workspace's category vocabulary: the category names in use on |
| `ListCategorisationFilters` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/filters` | List the attribute values the workspace's checks actually have — the |
| `ListCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/list` | List the workspace's checks with their resolved categories. Returns every |
| `BatchGetRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/batch-get` | Read specific rules by id — typically to resolve the `rule_id` a |
| `GetRecategorisationStatus` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/recategorisation-status` | Progress of the re-categorisation sweep a rule change schedules. The sweep |
| `ListRuleMatches` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/matches` | Report how many checks each rule currently categorises, with a sample of |
| `ListRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/list` | List the rules that apply to the caller's workspace — its own rules plus |
| `PreviewRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/preview` | Report what a candidate rule would change, without saving it. |
## Read Saved Views
`SCOPE_SAVED_VIEW_READ`
View saved views the caller has access to.
| Method | Description |
| - | - |
| `BatchGet` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/batch-get` | Fetch multiple saved views by id. Ids the caller cannot see, or that do not |
| `List` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/list` | List the saved views the calling user can see. By default returns every |
| `SetPin` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/set-pin` | Pin or unpin a saved view to the calling user's main screen. Pinning is a |
## Create and Edit Saved Views
`SCOPE_SAVED_VIEW_EDIT`
Create, update, and delete the caller's own saved views.
| Method | Description |
| - | - |
| `Delete` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/delete` | Delete a saved view. Idempotent — deleting an already-deleted or unknown |
| `Upsert` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1` | Create or update a saved view. |
## Manage All Saved Views in the Workspace
`SCOPE_SAVED_VIEW_ADMIN`
Manage all saved views in the workspace, including those owned by other users.
| Method | Description |
| - | - |
| `Delete` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/delete` | Delete a saved view. Idempotent — deleting an already-deleted or unknown |
| `Upsert` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1` | Create or update a saved view. |
## Read Folders
`SCOPE_FOLDERS_READ`
Browse the folder hierarchy and list the entities it contains.
| Method | Description |
| - | - |
| `Browse` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/browse` `GET /api/entities/folders/v1/browse` | Browse the hierarchy one level at a time. With no `parent`, returns the |
| `GetFolderOf` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/folder-of` `GET /api/entities/folders/v1/folder-of` | Given entity ids, return the folder breadcrumb(s) each entity lives in — |
| `ListMembers` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/members` `GET /api/entities/folders/v1/members` | List the entity ids contained in a folder, paginated. Direct members only by |
## Edit Folders
`SCOPE_FOLDERS_EDIT`
Create, update, and delete folders and their placements.
*No public gRPC/REST methods require this scope directly (e.g. MCP tools or future methods).*
## Read Deliveries
`SCOPE_DELIVERY_READ`
Inspect notifications sent to your integrations, including the requests made and the responses received.
| Method | Description |
| - | - |
| `BatchGetDeliveries` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/by-id` | Fetch deliveries by id. Ids that do not exist, or have passed out of the |
| `GetDelivery` *synq.deliveries.v1.DeliveriesService* | Fetch a single delivery by id. `BatchGetDeliveries` does the same for many |
| `ListAttempts` *synq.deliveries.v1.DeliveriesService* `GET /api/deliveries/v1/{delivery_id}/attempts` | List every attempt made for one delivery, oldest first. A delivery is |
| `ListDeliveries` *synq.deliveries.v1.DeliveriesService* `GET /api/deliveries/v1` | List deliveries, newest event first. With no `integration_id` this is the |
| `SummariseDeliveries` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/summarise` | Count deliveries over a window, bucketed by outcome, by skip reason and by |
## Send and Replay Deliveries
`SCOPE_DELIVERY_EDIT`
Send a test notification to an integration and replay an earlier delivery.
| Method | Description |
| - | - |
| `Redeliver` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/{delivery_id}/redeliver` | Send an earlier delivery again, to the same integration, with the same |
| `SendTestDelivery` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/test` | Send a test delivery to an integration, to confirm the endpoint is reachable |
## Read Recommendations
`SCOPE_RECOMMENDATIONS_READ`
View the changes Coalesce Quality recommends making in your workspace and the systems it connects to, and what was decided about them.
| Method | Description |
| - | - |
| `BatchGetRecommendations` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/by-id` | Fetch recommendations by id. Ids that are unknown, belong to another |
| `ListRecommendations` *synq.recommendations.v1.RecommendationsService* `GET /api/recommendations/v1` | List the ids of the workspace's recommendations, optionally limited by |
## Manage Recommendations
`SCOPE_RECOMMENDATIONS_EDIT`
Dismiss, ignore and restore recommendations.
| Method | Description |
| - | - |
| `BatchGetRecommendations` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/by-id` | Fetch recommendations by id. Ids that are unknown, belong to another |
| `DismissRecommendation` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/{recommendation_id}/dismiss` | Dismiss a recommendation until something new is found about it. Replaces |
| `IgnoreRecommendation` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/{recommendation_id}/ignore` | Ignore a recommendation for good, whatever is found about it later. |
| `ListRecommendations` *synq.recommendations.v1.RecommendationsService* `GET /api/recommendations/v1` | List the ids of the workspace's recommendations, optionally limited by |
| `RestoreRecommendation` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/{recommendation_id}/restore` | Remove the dismissal or ignore standing against a recommendation, so it |
## Read Platform Users
`SCOPE_PLATFORM_USERS_READ`
View the logins on your connected platforms, such as warehouse users and service accounts, and what each platform states about them.
| Method | Description |
| - | - |
| `GetActorSummary` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-summary` | Read what one actor read, built and wrote over the window, what its queries cost per compute and |
| `ListEntityActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actors` | Read who read and wrote each asked entity over the window: the jobs, the service accounts, the |
| `ListPersonActivity` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/person` | Read what one person's logins read and wrote over the window, per entity, direction and measure. |
| `BatchGetPlatformUsers` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/batch-get` | Read logins by platform user id. Ids no integration declared and with no activity in the window |
| `ListLinkSuggestions` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/link-suggestions` | Suggest which workspace member each unclassified login may belong to, by matching the login |
| `ListPlatformUsers` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/list` | List the workspace's logins by their share of the workspace's activity, highest first. The first |
## Link Platform Users
`SCOPE_PLATFORM_USER_LINK`
Link logins on your connected platforms to workspace members, so their reads count as that member's.
| Method | Description |
| - | - |
| `DeleteUserAliases` *synq.users.v1.UsersService* `POST /api/users/v1/aliases/delete` | Removes aliases from users. |
| `UpsertUserAliases` *synq.users.v1.UsersService* `PUT /api/users/v1/aliases` | Links aliases to users. |
## Read Service Accounts
`SCOPE_SERVICE_ACCOUNTS_READ`
View service accounts, person links, popularity policies and suggested service accounts.
| Method | Description |
| - | - |
| `BatchGetServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/batch-get` | Read service accounts by id, built-in ids included. Ids that do not exist are left out. |
| `ListServiceAccountSuggestions` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/suggestions` | Suggest service accounts for the logins the evidence says are not a person, each as a change set |
| `ListServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/list` | List every workspace service account, then every built-in one, in the order they apply, and the |
## Manage Service Accounts
`SCOPE_SERVICE_ACCOUNTS_EDIT`
Create and change service accounts, person links and popularity policies.
| Method | Description |
| - | - |
| `ApplyServiceAccountChanges` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/apply` | Write a planned change set, all of it or nothing. It is refused with FAILED\_PRECONDITION when the |
| `BatchGetServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/batch-get` | Read service accounts by id, built-in ids included. Ids that do not exist are left out. |
| `ListServiceAccountSuggestions` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/suggestions` | Suggest service accounts for the logins the evidence says are not a person, each as a change set |
| `ListServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/list` | List every workspace service account, then every built-in one, in the order they apply, and the |
| `PlanServiceAccountChanges` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/plan` | Report what a change set would change, without writing it: every login slice whose |
## Endpoint reference
Every public API method a customer token can call, and the scopes that satisfy it. A token needs any one of the listed scopes.
| Method | Scopes |
| - | - |
| `GenerateConnectionsYaml` *synq.agent.dwh.v1.ConnectionsService* | `SCOPE_MCP_READ` `SCOPE_EXTENSIONS_MANAGE` `SCOPE_ENTITY_READ` |
| `ListConnections` *synq.agent.dwh.v1.ConnectionsService* | `SCOPE_MCP_READ` `SCOPE_EXTENSIONS_MANAGE` `SCOPE_ENTITY_READ` |
| `GetAuditLog` *synq.agent.recon.v1.AuditLogService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `IngestAuditLog` *synq.agent.recon.v1.AuditLogService* | `SCOPE_RECON_EDIT` |
| `ListAuditLogs` *synq.agent.recon.v1.AuditLogService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `BatchReconciliationRunState` *synq.agent.recon.v1.RunStateService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `CancelReconciliationRun` *synq.agent.recon.v1.RunStateService* | `SCOPE_RECON_EDIT` |
| `ListReconciliationRunState` *synq.agent.recon.v1.RunStateService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `DeleteSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_EDIT` |
| `GetSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `GetSuiteConfigVersion` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `ListSuiteConfigVersions` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `ListSuiteConfigs` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `SubmitAdhocSuite` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_EDIT` |
| `UpsertSuiteConfig` *synq.agent.recon.v1.SuiteConfigService* | `SCOPE_RECON_EDIT` |
| `GetSuiteDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `ListSuiteDeploymentHistory` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `ListSuiteDeployments` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_READ` `SCOPE_MCP_READ` |
| `PauseDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `PromoteSuite` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `ResumeDeployment` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `TriggerDeployment` *synq.agent.recon.v1.SuiteDeploymentService* `POST /api/recon/v1/deployments/trigger` | `SCOPE_RECON_PROMOTE` |
| `UnpromoteSuite` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `UpdateDeploymentAnnotations` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `UpdateDeploymentCaseImpacts` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `UpdateDeploymentCategories` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `UpdateDeploymentMappings` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `UpdateDeploymentSchedule` *synq.agent.recon.v1.SuiteDeploymentService* | `SCOPE_RECON_PROMOTE` |
| `ParseYaml` *synq.agent.recon.v1.YamlService* `POST /api/agent/recon/v1/yaml/parse` | — |
| `RenderYaml` *synq.agent.recon.v1.YamlService* `POST /api/agent/recon/v1/yaml/render` | — |
| `ValidateSuite` *synq.agent.recon.v1.YamlService* `POST /api/agent/recon/v1/yaml/validate` | — |
| `SubmitFeatureRequest` *synq.agent.sre.v1.FeatureRequestService* `POST /api/feature-request/v1/submit` | `SCOPE_MCP_READ` `SCOPE_ENTITY_READ` `SCOPE_ISSUES_READ` `SCOPE_INCIDENTS_READ` `SCOPE_LINEAGE_READ` `SCOPE_MONITORS_READ` `SCOPE_EXECUTION_READ` `SCOPE_CODE_READ` `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_DATACHECKS_TESTSUGGESTIONS_READ` |
| `AcceptPromptSuggestion` *synq.agent.sre.v1.SreAgentService* | `SCOPE_INTEGRATION_EDIT` |
| `DismissPromptSuggestion` *synq.agent.sre.v1.SreAgentService* | `SCOPE_INTEGRATION_EDIT` |
| `GetConfig` *synq.agent.sre.v1.SreAgentService* | `SCOPE_INTEGRATION_READ` `SCOPE_MCP_READ` |
| `ListPromptSuggestions` *synq.agent.sre.v1.SreAgentService* | `SCOPE_INTEGRATION_READ` `SCOPE_MCP_READ` |
| `RecordPromptSuggestion` *synq.agent.sre.v1.SreAgentService* | `SCOPE_INTEGRATION_EDIT` |
| `BatchSuggestionStates` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_READ` `SCOPE_MCP_READ` |
| `GetActiveSuggestionStatesFor` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_READ` `SCOPE_MCP_READ` |
| `ListSuggestionStates` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_READ` `SCOPE_MCP_READ` |
| `MarkAsCancelled` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `MarkAsCompleted` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `MarkAsFailed` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `MarkAsRunning` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `SuggestFor` *synq.agent.sre.v1.TestsSuggestionStateService* | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `BatchLatestConclusions` *synq.agent.sre.v1.TriageService* `GET /api/triage/v1/conclusions` | `SCOPE_MCP_READ` |
| `BatchTriageState` *synq.agent.sre.v1.TriageService* `GET /api/triage/v1/states` | `SCOPE_MCP_READ` |
| `ListTriageState` *synq.agent.sre.v1.TriageService* `GET /api/triage/v1/statuses` | `SCOPE_MCP_READ` |
| `BatchGet` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1/get` | `SCOPE_ALERTS_READ` |
| `Create` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1` | `SCOPE_ALERTS_EDIT` |
| `Delete` *synq.alerts.services.v1.AlertsService* `DELETE /api/alerts/v1` | `SCOPE_ALERTS_EDIT` |
| `List` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1/list` | `SCOPE_ALERTS_READ` |
| `ToggleEnabled` *synq.alerts.services.v1.AlertsService* `POST /api/alerts/v1/toggle` | `SCOPE_ALERTS_EDIT` |
| `Update` *synq.alerts.services.v1.AlertsService* `PUT /api/alerts/v1` | `SCOPE_ALERTS_EDIT` |
| `BatchGet` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/get` | `SCOPE_ALERTS_READ` `SCOPE_MCP_READ` |
| `Create` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `Delete` *synq.alerts.services.v2.AlertsService* `DELETE /api/alerts/v2` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `GetIncidentOwnerAlert` *synq.alerts.services.v2.AlertsService* `GET /api/alerts/v2/incident-owner` | `SCOPE_ALERTS_READ` `SCOPE_MCP_READ` |
| `List` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/list` | `SCOPE_ALERTS_READ` `SCOPE_MCP_READ` |
| `Rename` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/rename` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `SendTestAlert` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/test` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `ToggleEnabled` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/toggle` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `ToggleIncidentOwnerAlert` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/incident-owner` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `UpdateSettings` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/settings` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `UpdateTargets` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/targets` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `UpdateTemplate` *synq.alerts.services.v2.AlertsService* `POST /api/alerts/v2/template` | `SCOPE_ALERTS_EDIT` |
| `Upsert` *synq.alerts.services.v2.AlertsService* `PUT /api/alerts/v2` | `SCOPE_ALERTS_EDIT` `SCOPE_MCP_WRITE` |
| `Iam` *synq.auth.iam.v1.IamService* `GET /api/iam/v1` | — |
| `BatchDeleteSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `DELETE /api/datachecks/sql-test-deployment-rules/v1/batch` | `SCOPE_DATACHECKS_SQLTESTS_EDIT` |
| `BatchDeploySqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/batch/deployed` | `SCOPE_DATACHECKS_SQLTESTS_EDIT` |
| `BatchGetSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/by-id` | `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_MCP_READ` |
| `DeleteSqlTestDeploymentRule` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `DELETE /api/datachecks/sql-test-deployment-rules/v1/{id}` | `SCOPE_DATACHECKS_SQLTESTS_EDIT` `SCOPE_MCP_WRITE` |
| `DeploySqlTestDeploymentRule` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `POST /api/datachecks/sql-test-deployment-rules/v1/deployed` | `SCOPE_DATACHECKS_SQLTESTS_EDIT` `SCOPE_MCP_WRITE` |
| `GetSqlTestDeployDeleteOverview` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1/{id}/deleted` | `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_MCP_READ` |
| `GetSqlTestDeployOverview` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1/overview` | `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_MCP_READ` |
| `ListSqlTestDeploymentRules` *synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService* `GET /api/datachecks/sql-test-deployment-rules/v1` | `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_MCP_READ` |
| `BatchDeleteSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `DELETE /api/datachecks/sqltests/v1` | `SCOPE_DATACHECKS_SQLTESTS_EDIT` |
| `BatchGetSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `POST /api/datachecks/sqltests/v1/by-id` | `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_MCP_READ` |
| `BatchUpsertSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `POST /api/datachecks/sqltests/v1` | `SCOPE_DATACHECKS_SQLTESTS_EDIT` |
| `GetSqlTestAudit` *synq.datachecks.sqltests.v1.SqlTestsService* | `SCOPE_DATACHECKS_SQLTESTS_AUDIT_READ` |
| `ListSqlTests` *synq.datachecks.sqltests.v1.SqlTestsService* `GET /api/datachecks/sqltests/v1` | `SCOPE_DATACHECKS_SQLTESTS_READ` `SCOPE_MCP_READ` |
| `BatchAcceptTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `PATCH /api/datachecks/testsuggestions/v1/accepted` | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `BatchCreateTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `POST /api/datachecks/testsuggestions/v1` | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` `SCOPE_MCP_WRITE` |
| `BatchGetTestSuggestionsForEntities` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `GET /api/datachecks/testsuggestions/v1` | `SCOPE_DATACHECKS_TESTSUGGESTIONS_READ` `SCOPE_MCP_READ` |
| `BatchRejectTestSuggestions` *synq.datachecks.testsuggestions.v1.TestSuggestionsService* `PATCH /api/datachecks/testsuggestions/v1/rejected` | `SCOPE_DATACHECKS_TESTSUGGESTIONS_EDIT` |
| `TriggerDatachecks` *synq.datachecks.v1.TriggerService* `POST /api/datachecks/scheduler/v1/trigger` | `SCOPE_DATACHECKS_TRIGGER` |
| `Delete` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/delete` | `SCOPE_DATAPRODUCT_EDIT` |
| `Get` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/get` | `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `List` *synq.dataproducts.v1.DataproductsService* `GET /api/dataproducts/v1` | `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `RemoveDefinitionPart` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/definition/part/delete` | `SCOPE_DATAPRODUCT_EDIT` |
| `SetDefinition` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/definition` | `SCOPE_DATAPRODUCT_EDIT` |
| `Upsert` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1` | `SCOPE_DATAPRODUCT_EDIT` |
| `UpsertDefinitionPart` *synq.dataproducts.v1.DataproductsService* `POST /api/dataproducts/v1/definition/part` | `SCOPE_DATAPRODUCT_EDIT` |
| `BatchGet` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/batch-get` | `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `Delete` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/delete` | `SCOPE_DATAPRODUCT_EDIT` `SCOPE_MCP_WRITE` |
| `List` *synq.dataproducts.v2.DataproductsService* `GET /api/dataproducts/v2` | `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `ListMembers` *synq.dataproducts.v2.DataproductsService* `GET /api/dataproducts/v2/{id}/members` | `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `RemoveDefinitionPart` *synq.dataproducts.v2.DataproductsService* `DELETE /api/dataproducts/v2/{id}/definition/parts/{part_id}` | `SCOPE_DATAPRODUCT_EDIT` |
| `SetDefinition` *synq.dataproducts.v2.DataproductsService* `PUT /api/dataproducts/v2/{id}/definition` | `SCOPE_DATAPRODUCT_EDIT` |
| `Upsert` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2` | `SCOPE_DATAPRODUCT_EDIT` `SCOPE_MCP_WRITE` |
| `UpsertDefinitionPart` *synq.dataproducts.v2.DataproductsService* `POST /api/dataproducts/v2/{id}/definition/parts` | `SCOPE_DATAPRODUCT_EDIT` |
| `BatchGetDeliveries` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/by-id` | `SCOPE_DELIVERY_READ` |
| `GetDelivery` *synq.deliveries.v1.DeliveriesService* | `SCOPE_DELIVERY_READ` |
| `ListAttempts` *synq.deliveries.v1.DeliveriesService* `GET /api/deliveries/v1/{delivery_id}/attempts` | `SCOPE_DELIVERY_READ` |
| `ListDeliveries` *synq.deliveries.v1.DeliveriesService* `GET /api/deliveries/v1` | `SCOPE_DELIVERY_READ` |
| `Redeliver` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/{delivery_id}/redeliver` | `SCOPE_DELIVERY_EDIT` |
| `SendTestDelivery` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/test` | `SCOPE_DELIVERY_EDIT` |
| `SummariseDeliveries` *synq.deliveries.v1.DeliveriesService* `POST /api/deliveries/v1/summarise` | `SCOPE_DELIVERY_READ` |
| `AddDomainMembers` *synq.domains.members.v1.DomainMembersService* `POST /api/domains/members/v1/{domain_id}/members` | `SCOPE_USER_ASSIGN` |
| `ListDomainMembers` *synq.domains.members.v1.DomainMembersService* `GET /api/domains/members/v1/{domain_id}/members` | `SCOPE_USER_READ` |
| `ListUserDomains` *synq.domains.members.v1.DomainMembersService* `GET /api/domains/members/v1/users/{user_id}/domains` | `SCOPE_USER_READ` |
| `RemoveDomainMembers` *synq.domains.members.v1.DomainMembersService* `POST /api/domains/members/v1/{domain_id}/members/remove` | `SCOPE_USER_ASSIGN` |
| `SetDefaultUserDomain` *synq.domains.members.v1.DomainMembersService* `POST /api/domains/members/v1/{domain_id}/default-for-user` | `SCOPE_USER_READ` |
| `BatchGet` *synq.domains.v1.DomainsService* `POST /api/domains/v1/batch-get` | `SCOPE_DOMAIN_READ` |
| `Delete` *synq.domains.v1.DomainsService* `POST /api/domains/v1/delete` | `SCOPE_DOMAIN_EDIT` |
| `FetchDomainWithSubDomains` *synq.domains.v1.DomainsService* `GET /api/domains/v1/{domain_id}/sub-domains` | `SCOPE_DOMAIN_READ` |
| `GetUsage` *synq.domains.v1.DomainsService* `GET /api/domains/v1/usage` | `SCOPE_USAGE_GET` |
| `List` *synq.domains.v1.DomainsService* `GET /api/domains/v1` | `SCOPE_DOMAIN_READ` |
| `RemoveDefinitionPart` *synq.domains.v1.DomainsService* `DELETE /api/domains/v1/{domain_id}/definition/parts/{part_id}` | `SCOPE_DOMAIN_EDIT` |
| `SetDefinition` *synq.domains.v1.DomainsService* `PUT /api/domains/v1/{domain_id}/definition` | `SCOPE_DOMAIN_EDIT` |
| `Upsert` *synq.domains.v1.DomainsService* `POST /api/domains/v1` | `SCOPE_DOMAIN_EDIT` |
| `UpsertDefinitionPart` *synq.domains.v1.DomainsService* `POST /api/domains/v1/{domain_id}/definition/parts` | `SCOPE_DOMAIN_EDIT` |
| `BatchGet` *synq.domains.v2.DomainsService* `POST /api/domains/v2/batch-get` | `SCOPE_DOMAIN_READ` `SCOPE_MCP_READ` |
| `Delete` *synq.domains.v2.DomainsService* `POST /api/domains/v2/delete` | `SCOPE_DOMAIN_EDIT` `SCOPE_MCP_WRITE` |
| `FetchDomainWithSubDomains` *synq.domains.v2.DomainsService* `GET /api/domains/v2/{id}/sub-domains` | `SCOPE_DOMAIN_READ` `SCOPE_MCP_READ` |
| `GetUsage` *synq.domains.v2.DomainsService* `GET /api/domains/v2/usage` | `SCOPE_USAGE_GET` |
| `List` *synq.domains.v2.DomainsService* `GET /api/domains/v2` | `SCOPE_DOMAIN_READ` `SCOPE_MCP_READ` |
| `ListMembers` *synq.domains.v2.DomainsService* `GET /api/domains/v2/{id}/members` | `SCOPE_DOMAIN_READ` `SCOPE_MCP_READ` |
| `RemoveDefinitionPart` *synq.domains.v2.DomainsService* `DELETE /api/domains/v2/{id}/definition/parts/{part_id}` | `SCOPE_DOMAIN_EDIT` |
| `SetDefinition` *synq.domains.v2.DomainsService* `PUT /api/domains/v2/{id}/definition` | `SCOPE_DOMAIN_EDIT` |
| `Upsert` *synq.domains.v2.DomainsService* `POST /api/domains/v2` | `SCOPE_DOMAIN_EDIT` `SCOPE_MCP_WRITE` |
| `UpsertDefinitionPart` *synq.domains.v2.DomainsService* `POST /api/domains/v2/{id}/definition/parts` | `SCOPE_DOMAIN_EDIT` |
| `BatchGetEntityUsage` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/usage` | `SCOPE_ENTITY_READ` `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `BatchGetPopularity` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/popularity` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetActivityOverview` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/overview` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetActorSummary` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-summary` | `SCOPE_PLATFORM_USERS_READ` `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `GetEntityActivitySeries` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/series` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListAccessSignals` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/signals` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actor-list` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListEntityActors` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/actors` | `SCOPE_PLATFORM_USERS_READ` `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `ListPersonActivity` *synq.entities.activity.v1alpha.ActivityService* `POST /api/entities/activity/v1alpha/person` | `SCOPE_PLATFORM_USERS_READ` `SCOPE_MCP_READ` |
| `BatchGetEntityAnnotations` *synq.entities.annotations.v1.AnnotationsService* `POST /api/entities/annotations/v1/batchGet` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListAnnotations` *synq.entities.annotations.v1.AnnotationsService* `GET /api/entities/annotations/v1` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchChange` *synq.entities.changes.v1.ChangesService* `POST /api/changes/v1/by-id` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `List` *synq.entities.changes.v1.ChangesService* `GET /api/changes/v1` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `DeleteCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `POST /api/entities/checks/v1/categories:delete` | `SCOPE_POLICIES_EDIT` |
| `ListCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `GET /api/entities/checks/v1/categories` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` |
| `UpsertCheckCategories` *synq.entities.checks.v1.ChecksCategoriesService* `POST /api/entities/checks/v1/categories:upsert` | `SCOPE_POLICIES_EDIT` |
| `GetCoverage` *synq.entities.checks.v1.ChecksCoverageService* | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchGetCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/batch-get` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `DeleteCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/delete` | `SCOPE_POLICIES_EDIT` `SCOPE_MCP_WRITE` |
| `ListCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/vocabulary` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `ListCategorisationFilters` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/filters` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `ListCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories/list` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `UpsertCheckCategories` *synq.entities.checks.v2.ChecksCategoriesService* `POST /api/entities/v2/checks/categories` | `SCOPE_POLICIES_EDIT` `SCOPE_MCP_WRITE` |
| `BatchGetRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/batch-get` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `DeleteRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/delete` | `SCOPE_POLICIES_EDIT` `SCOPE_MCP_WRITE` |
| `GetRecategorisationStatus` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/recategorisation-status` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `ListRuleMatches` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/matches` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `ListRules` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/list` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `PreviewRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules/preview` | `SCOPE_POLICIES_READ` `SCOPE_POLICIES_EDIT` `SCOPE_MCP_READ` |
| `UpsertRule` *synq.entities.checks.v2.ChecksCategorisationRulesService* `POST /api/entities/v2/checks/categorisation-rules` | `SCOPE_POLICIES_EDIT` `SCOPE_MCP_WRITE` |
| `ListCode` *synq.entities.code.v1.CodeService* `GET /api/code/v1` | `SCOPE_CODE_READ` `SCOPE_MCP_READ` |
| `GetTableConstraints` *synq.entities.constraints.v1.TableConstraintsService* `GET /api/constraints/v1/table-constraints` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchDatabaseCoordinates` *synq.entities.coordinates.v1.DatabaseCoordinatesService* `POST /api/coordinates/v1/database/by-id` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchIdsByCoordinates` *synq.entities.coordinates.v1.DatabaseCoordinatesService* `POST /api/coordinates/v1/database/by-fqn` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `DeleteCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `DELETE /api/entities/custom/v1/check-relationships` | `SCOPE_ENTITY_EDIT` |
| `ListCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `POST /api/entities/custom/v1/check-relationships/list` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `UpsertCheckRelationships` *synq.entities.custom.v1.ChecksRelationshipsService* `POST /api/entities/custom/v1/check-relationships` | `SCOPE_ENTITY_EDIT` |
| `DeleteEntity` *synq.entities.custom.v1.EntitiesService* `DELETE /api/entities/custom/v1/entities` | `SCOPE_ENTITY_EDIT` |
| `ListEntities` *synq.entities.custom.v1.EntitiesService* `GET /api/entities/custom/v1/entities` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `UpsertEntity` *synq.entities.custom.v1.EntitiesService* `POST /api/entities/custom/v1/entities` | `SCOPE_ENTITY_EDIT` |
| `UpsertExecution` *synq.entities.custom.v1.EntityExecutionsService* `POST /api/entities/custom/executions/v1` | `SCOPE_EXECUTION_EDIT` |
| `UpsertLogEntry` *synq.entities.custom.v1.EntityExecutionsService* `POST /api/entities/custom/executions/v1/log-entry` | `SCOPE_EXECUTION_EDIT` `SCOPE_EXECUTION_LOG_EDIT` |
| `DeleteEntityFeature` *synq.entities.custom.v1.FeaturesService* `DELETE /api/entities/custom/v1/features` | `SCOPE_ENTITY_EDIT` |
| `ListEntityFeatures` *synq.entities.custom.v1.FeaturesService* `POST /api/entities/custom/v1/features/list` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `UpsertEntityFeature` *synq.entities.custom.v1.FeaturesService* `POST /api/entities/custom/v1/features` | `SCOPE_ENTITY_EDIT` |
| `DeleteEntitiesGroup` *synq.entities.custom.v1.GroupsService* `DELETE /api/entities/custom/v1/groups` | `SCOPE_ENTITY_EDIT` |
| `ListEntityGroups` *synq.entities.custom.v1.GroupsService* `POST /api/entities/custom/v1/groups/list` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `UpsertEntitiesGroup` *synq.entities.custom.v1.GroupsService* `POST /api/entities/custom/v1/groups` | `SCOPE_ENTITY_EDIT` |
| `DeleteRelationships` *synq.entities.custom.v1.RelationshipsService* `DELETE /api/entities/custom/v1/relationships` | `SCOPE_LINEAGE_EDIT` |
| `ListRelationships` *synq.entities.custom.v1.RelationshipsService* `POST /api/entities/custom/v1/relationships/list` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `UpsertRelationships` *synq.entities.custom.v1.RelationshipsService* `POST /api/entities/custom/v1/relationships` | `SCOPE_LINEAGE_EDIT` |
| `DeleteType` *synq.entities.custom.v1.TypesService* `DELETE /api/entities/custom/v1/types` | `SCOPE_ENTITY_TYPE_EDIT` |
| `ListTypes` *synq.entities.custom.v1.TypesService* `POST /api/entities/custom/v1/types/list` | `SCOPE_ENTITY_READ` `SCOPE_ENTITY_TYPE_READ` |
| `UpsertType` *synq.entities.custom.v1.TypesService* `POST /api/entities/custom/v1/types` | `SCOPE_ENTITY_TYPE_EDIT` |
| `BatchGetEntities` *synq.entities.entities.v1.EntitiesService* `POST /api/entities/v1/by-id` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetEntity` *synq.entities.entities.v1.EntitiesService* | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListEntities` *synq.entities.entities.v1.EntitiesService* `GET /api/entities/v1` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `SearchEntities` *synq.entities.entities.v1.EntitiesService* `GET /api/entities/v1/search` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `UpsertExecution` *synq.entities.executions.v1.EntityExecutionsService* `POST /api/executions/v1` | `SCOPE_EXECUTION_EDIT` |
| `UpsertLogEntry` *synq.entities.executions.v1.EntityExecutionsService* `POST /api/executions/v1/log-entry` | `SCOPE_EXECUTION_EDIT` `SCOPE_EXECUTION_LOG_EDIT` |
| `BatchGetExecutions` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/by-id` | `SCOPE_EXECUTION_READ` `SCOPE_MCP_READ` |
| `GetExecutionsSummary` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/summary` | `SCOPE_EXECUTION_READ` `SCOPE_MCP_READ` |
| `GetLatestExecutions` *synq.entities.executions.v2.EntityExecutionsService* `POST /api/executions/v2/latest` | `SCOPE_EXECUTION_READ` `SCOPE_MCP_READ` |
| `ListExecutions` *synq.entities.executions.v2.EntityExecutionsService* `GET /api/executions/v2/list` | `SCOPE_EXECUTION_READ` `SCOPE_MCP_READ` |
| `Browse` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/browse` `GET /api/entities/folders/v1/browse` | `SCOPE_FOLDERS_READ` `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetFolderOf` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/folder-of` `GET /api/entities/folders/v1/folder-of` | `SCOPE_FOLDERS_READ` `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListMembers` *synq.entities.folders.v1.FoldersService* `POST /api/entities/folders/v1/members` `GET /api/entities/folders/v1/members` | `SCOPE_FOLDERS_READ` `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetImpact` *synq.entities.impact.v1.ImpactService* `POST /api/impact/v1` | `SCOPE_ENTITY_READ` `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `BatchGetFoldNodes` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/nodes/batch-get` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `BatchGetFolds` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/batch-get` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `CreateFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha` | `SCOPE_LINEAGE_EDIT` |
| `DeleteFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/delete` | `SCOPE_LINEAGE_EDIT` |
| `ListFolds` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `GET /api/lineage/folds/v1alpha` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `PreviewFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/preview` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `RenameFold` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/rename` | `SCOPE_LINEAGE_EDIT` |
| `SetFoldEnabled` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/enabled` | `SCOPE_LINEAGE_EDIT` |
| `UpdateFoldMatcher` *synq.entities.lineage.folds.v1alpha.LineageFoldsService* `POST /api/lineage/folds/v1alpha/{id}/matcher` | `SCOPE_LINEAGE_EDIT` |
| `GetLineage` *synq.entities.lineage.v1.LineageService* `POST /api/lineage/v1` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `BatchOrchestration` *synq.entities.orchestration.v1.OrchestrationService* `GET /api/orchestration/v1` | `SCOPE_LINEAGE_READ` `SCOPE_MCP_READ` |
| `BatchGetPlatformUsers` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/batch-get` | `SCOPE_PLATFORM_USERS_READ` `SCOPE_MCP_READ` |
| `ListLinkSuggestions` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/link-suggestions` | `SCOPE_PLATFORM_USERS_READ` `SCOPE_MCP_READ` |
| `ListPlatformUsers` *synq.entities.platformusers.v1alpha.PlatformUsersService* `POST /api/entities/platform-users/v1alpha/list` | `SCOPE_PLATFORM_USERS_READ` `SCOPE_MCP_READ` |
| `ApplyServiceAccountChanges` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/apply` | `SCOPE_SERVICE_ACCOUNTS_EDIT` `SCOPE_MCP_WRITE` |
| `BatchGetServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/batch-get` | `SCOPE_SERVICE_ACCOUNTS_READ` `SCOPE_SERVICE_ACCOUNTS_EDIT` `SCOPE_MCP_READ` |
| `ListServiceAccountSuggestions` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/suggestions` | `SCOPE_SERVICE_ACCOUNTS_READ` `SCOPE_SERVICE_ACCOUNTS_EDIT` `SCOPE_MCP_READ` |
| `ListServiceAccounts` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/list` | `SCOPE_SERVICE_ACCOUNTS_READ` `SCOPE_SERVICE_ACCOUNTS_EDIT` `SCOPE_MCP_READ` |
| `PlanServiceAccountChanges` *synq.entities.platformusers.v1alpha.ServiceAccountsService* `POST /api/entities/service-accounts/v1alpha/plan` | `SCOPE_SERVICE_ACCOUNTS_EDIT` `SCOPE_MCP_WRITE` |
| `BatchResolveIdentifiers` *synq.entities.resolve.v1.IdentifierResolveService* `POST /api/resolve/v1/by-id` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchSchemaMismatches` *synq.entities.schemas.v1.SchemaMismatchesService* `POST /api/schema-mismatches/v1` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetSchema` *synq.entities.schemas.v1.SchemasService* `GET /api/schema/v1` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchSqlDetails` *synq.entities.sql\_insights.v1.SqlInsightsService* `POST /api/sql-insights/v1/details:batch` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListJoinPatterns` *synq.entities.sql\_insights.v1.SqlInsightsService* `GET /api/sql-insights/v1/join-patterns` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListSqlConstructs` *synq.entities.sql\_insights.v1.SqlInsightsService* `GET /api/sql-insights/v1/constructs` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchGetIncidents` *synq.entities.status.v1.EntityIncidentsService* `POST /api/status/v1/incidents/by-id` | `SCOPE_STATUS_READ` `SCOPE_MCP_READ` |
| `GetIncidents` *synq.entities.status.v1.EntityIncidentsService* | `SCOPE_STATUS_READ` `SCOPE_MCP_READ` |
| `BatchGetIssuesStatus` *synq.entities.status.v1.EntityIssuesService* `POST /api/status/v1/issues/by-id` | `SCOPE_STATUS_READ` `SCOPE_MCP_READ` |
| `GetIssuesStatus` *synq.entities.status.v1.EntityIssuesService* | `SCOPE_STATUS_READ` |
| `ListEntityTraitDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/entity-traits/list` | `SCOPE_ENTITY_READ` `SCOPE_ENTITY_TYPE_READ` |
| `ListEntityTypeDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/entity-types/list` | `SCOPE_ENTITY_READ` `SCOPE_ENTITY_TYPE_READ` |
| `ListExecutionTypeDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/execution-types/list` | `SCOPE_ENTITY_READ` `SCOPE_ENTITY_TYPE_READ` |
| `ListPlatformDefs` *synq.entities.typedefs.v1.TypeDefsService* `POST /api/entities/typedefs/v1/platforms/list` | `SCOPE_ENTITY_READ` `SCOPE_ENTITY_TYPE_READ` |
| `Activate` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `PATCH /api/extensions/atlan/integrations/v1` | `SCOPE_EXTENSIONS_MANAGE` |
| `Get` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `GET /api/extensions/atlan/integrations/v1` | `SCOPE_INTEGRATION_READ` |
| `Ping` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `POST /api/extensions/atlan/integrations/v1/ping` | `SCOPE_EXTENSIONS_MANAGE` |
| `Remove` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `DELETE /api/extensions/atlan/integrations/v1` | `SCOPE_EXTENSIONS_MANAGE` |
| `Upsert` *synq.extensions.atlan.integrations.v1.AtlanIntegrationService* `POST /api/extensions/atlan/integrations/v1` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetAtlanConnections` *synq.extensions.atlan.provider.v1.AtlanProviderService* `GET /api/extensions/atlan/provider/v1/connections` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetAtlanDataProducts` *synq.extensions.atlan.provider.v1.AtlanProviderService* `GET /api/extensions/atlan/provider/v1/data-products` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetAtlanDomains` *synq.extensions.atlan.provider.v1.AtlanProviderService* `GET /api/extensions/atlan/provider/v1/domains` | `SCOPE_EXTENSIONS_MANAGE` |
| `FetchRuns` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/runs` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetConnectionMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/connection-mappings` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetDomainMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/domain-mappings` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetMappingToggles` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/mappings/toggle` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetProductMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `GET /api/extensions/atlan/workflows/v1/product-mappings` | `SCOPE_EXTENSIONS_MANAGE` |
| `RemoveConnectionMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `DELETE /api/extensions/atlan/workflows/v1/connection-mappings` | `SCOPE_EXTENSIONS_MANAGE` |
| `SetConnectionMappings` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `POST /api/extensions/atlan/workflows/v1/connection-mappings` | `SCOPE_EXTENSIONS_MANAGE` |
| `SetMappingToggles` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `POST /api/extensions/atlan/workflows/v1/mappings/toggle` | `SCOPE_EXTENSIONS_MANAGE` |
| `Synchronize` *synq.extensions.atlan.workflows.v1.AtlanWorkflowService* `POST /api/extensions/atlan/workflows/v1/synchronize` | `SCOPE_EXTENSIONS_MANAGE` |
| `GetDiffByChangeId` *synq.git.commits.v1.CommitsService* `GET /api/git/v1/change/{change_id}` | `SCOPE_CODE_READ` `SCOPE_MCP_READ` |
| `GetDiffByCommit` *synq.git.commits.v1.CommitsService* `GET /api/git/v1/commit/{commit_sha}` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `AddIssuesToIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/issues` | `SCOPE_INCIDENTS_EDIT` |
| `AssignIncidentOwner` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/assign-owner` | `SCOPE_INCIDENTS_EDIT` |
| `BatchGetIncidents` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/by-id` | `SCOPE_INCIDENTS_READ` `SCOPE_MCP_READ` |
| `CancelIncident` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/cancel` | `SCOPE_INCIDENTS_EDIT` |
| `CloseIncident` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/close` | `SCOPE_INCIDENTS_EDIT` |
| `CreateIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1` | `SCOPE_INCIDENTS_EDIT` `SCOPE_MCP_WRITE` |
| `DeleteComment` *synq.incidents.v1.IncidentsService* `DELETE /api/incidents/v1/{incident_id}/comment/{comment_id}` | `SCOPE_INCIDENTS_EDIT` `SCOPE_MCP_WRITE` |
| `ListIncidents` *synq.incidents.v1.IncidentsService* `GET /api/incidents/v1` | `SCOPE_INCIDENTS_READ` `SCOPE_MCP_READ` |
| `MergeIncidents` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/merge` | `SCOPE_INCIDENTS_EDIT` |
| `PostComment` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/comment` | `SCOPE_INCIDENTS_EDIT` `SCOPE_MCP_WRITE` |
| `RemoveIssuesFromIncident` *synq.incidents.v1.IncidentsService* `DELETE /api/incidents/v1/{incident_id}/issues` | `SCOPE_INCIDENTS_EDIT` |
| `RenameIncident` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/rename` | `SCOPE_INCIDENTS_EDIT` |
| `SplitIncident` *synq.incidents.v1.IncidentsService* `POST /api/incidents/v1/{incident_id}/split` | `SCOPE_INCIDENTS_EDIT` |
| `UpdateIncidentState` *synq.incidents.v1.IncidentsService* `PATCH /api/incidents/v1/{incident_id}/state` | `SCOPE_INCIDENTS_EDIT` |
| `BatchGetIntegrations` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/by-id` | `SCOPE_INTEGRATION_READ` `SCOPE_INTEGRATION_EDIT` |
| `CreateIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1` | `SCOPE_INTEGRATION_EDIT` |
| `DeleteIntegration` *synq.integrations.v1.IntegrationsService* `DELETE /api/integrations/v1/{integration_id}` | `SCOPE_INTEGRATION_EDIT` |
| `DisableIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/{integration_id}/disable` | `SCOPE_INTEGRATION_EDIT` |
| `EnableIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/{integration_id}/enable` | `SCOPE_INTEGRATION_EDIT` |
| `GetIntegration` *synq.integrations.v1.IntegrationsService* | `SCOPE_INTEGRATION_READ` `SCOPE_INTEGRATION_EDIT` |
| `GetIntegrationHealth` *synq.integrations.v1.IntegrationsService* | `SCOPE_INTEGRATION_READ` `SCOPE_INTEGRATION_EDIT` |
| `ListIntegrations` *synq.integrations.v1.IntegrationsService* `GET /api/integrations/v1` | `SCOPE_INTEGRATION_READ` `SCOPE_INTEGRATION_EDIT` |
| `RefreshIntegration` *synq.integrations.v1.IntegrationsService* `POST /api/integrations/v1/{integration_id}/refresh` | `SCOPE_INTEGRATION_EDIT` |
| `UpdateIntegration` *synq.integrations.v1.IntegrationsService* `PUT /api/integrations/v1/{integration_id}` | `SCOPE_INTEGRATION_EDIT` |
| `BatchGetIssues` *synq.issues.issues.v1.IssuesService* `POST /api/issues/v1/by-id` | `SCOPE_ISSUES_READ` `SCOPE_MCP_READ` |
| `ListIssues` *synq.issues.issues.v1.IssuesService* `GET /api/issues/v1` | `SCOPE_ISSUES_READ` `SCOPE_MCP_READ` |
| `MarkExpected` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-expected` | `SCOPE_ISSUES_EDIT` |
| `MarkFixed` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-fixed` | `SCOPE_ISSUES_EDIT` |
| `MarkInvestigating` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-investigating` | `SCOPE_ISSUES_EDIT` |
| `MarkNoActionNeeded` *synq.issues.issues.v1.IssuesService* `PATCH /api/issues/v1/{issue_id}/mark-no-action-needed` | `SCOPE_ISSUES_EDIT` |
| `PostComment` *synq.issues.issues.v1.IssuesService* `POST /api/issues/v1/{issue_id}/comment` | `SCOPE_ISSUES_EDIT` |
| `BatchGetIssues` *synq.issues.v2.IssuesService* `POST /api/issues/v2/by-id` | `SCOPE_ISSUES_READ` `SCOPE_MCP_READ` |
| `ClearStatus` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/clear-status` | `SCOPE_ISSUES_EDIT` |
| `DeleteComment` *synq.issues.v2.IssuesService* `DELETE /api/issues/v2/{issue_id}/comment/{comment_id}` | `SCOPE_ISSUES_EDIT` `SCOPE_MCP_WRITE` |
| `ListIssues` *synq.issues.v2.IssuesService* `GET /api/issues/v2` | `SCOPE_ISSUES_READ` `SCOPE_MCP_READ` |
| `MarkExpected` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-expected` | `SCOPE_ISSUES_EDIT` |
| `MarkFixed` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-fixed` | `SCOPE_ISSUES_EDIT` |
| `MarkInvestigating` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-investigating` | `SCOPE_ISSUES_EDIT` |
| `MarkNoActionNeeded` *synq.issues.v2.IssuesService* `PATCH /api/issues/v2/{issue_id}/mark-no-action-needed` | `SCOPE_ISSUES_EDIT` |
| `PostComment` *synq.issues.v2.IssuesService* `POST /api/issues/v2/{issue_id}/comment` | `SCOPE_ISSUES_EDIT` `SCOPE_MCP_WRITE` |
| `BatchGetEntityKnowledge` *synq.knowledge.v1alpha.KnowledgeService* `POST /api/knowledge/v1alpha/entities/by-id` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchGetKnowledgePages` *synq.knowledge.v1alpha.KnowledgeService* `POST /api/knowledge/v1alpha/pages/by-id` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListKnowledgePageLinks` *synq.knowledge.v1alpha.KnowledgeService* `GET /api/knowledge/v1alpha/pages/{page_id}/links` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `ListKnowledgePages` *synq.knowledge.v1alpha.KnowledgeService* `GET /api/knowledge/v1alpha/pages` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchDeleteDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `DELETE /api/monitors/automated/v1/deployment-rules/batch` | `SCOPE_MONITORS_AUTOMATIC_EDIT` |
| `BatchDeployDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/batch/deployed` | `SCOPE_MONITORS_AUTOMATIC_EDIT` |
| `BatchGetDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules/batch` | `SCOPE_MONITORS_AUTOMATIC_EDIT` |
| `BatchUpdateDeploymentRuleTitle` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `PATCH /api/monitors/automated/v1/deployment-rules` | `SCOPE_MONITORS_AUTOMATIC_EDIT` |
| `BatchUpsertDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/batch` | `SCOPE_MONITORS_AUTOMATIC_EDIT` |
| `DeleteDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `DELETE /api/monitors/automated/v1/deployment-rule/{id}` | `SCOPE_MONITORS_AUTOMATIC_EDIT` `SCOPE_MCP_WRITE` |
| `DeployDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules/deployed` | `SCOPE_MONITORS_AUTOMATIC_EDIT` `SCOPE_MCP_WRITE` |
| `GetDeployDeleteOverview` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rule/{id}/deleted` | `SCOPE_MONITORS_AUTOMATIC_EDIT` `SCOPE_MCP_READ` |
| `GetDeployOverview` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rule` | `SCOPE_MONITORS_AUTOMATIC_EDIT` `SCOPE_MCP_READ` |
| `ListDeploymentRules` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules` | `SCOPE_MONITORS_AUTOMATIC_EDIT` `SCOPE_MCP_READ` |
| `ListDeploymentRulesForAsset` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `GET /api/monitors/automated/v1/deployment-rules/{asset_path}` | `SCOPE_MONITORS_AUTOMATIC_EDIT` `SCOPE_MCP_READ` |
| `UpsertDeploymentRule` *synq.monitors.automated\_monitors.v1.DeploymentRulesService* `POST /api/monitors/automated/v1/deployment-rules` | `SCOPE_MONITORS_AUTOMATIC_EDIT` |
| `BatchCreateMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1` | `SCOPE_MONITORS_CUSTOM_EDIT` `SCOPE_MONITORS_CUSTOM_SQL_EDIT` |
| `BatchDeleteMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/delete` | `SCOPE_MONITORS_CUSTOM_EDIT` |
| `BatchUpdateMonitor` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `PUT /api/monitors/custom-monitors/v1` | `SCOPE_MONITORS_CUSTOM_EDIT` `SCOPE_MONITORS_CUSTOM_SQL_EDIT` |
| `BatchUpdateMonitorMode` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/mode` | `SCOPE_MONITORS_CUSTOM_EDIT` |
| `BatchUpdateMonitorSeverity` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/severity` | `SCOPE_MONITORS_CUSTOM_EDIT` |
| `ConfigChangesOverview` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `POST /api/monitors/custom-monitors/v1/config-changes` | `SCOPE_MONITORS_CUSTOM_EDIT` |
| `ListConfigsMonitors` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `GET /api/monitors/custom-monitors/v1/by-config` | `SCOPE_MONITORS_READ` |
| `ListMonitors` *synq.monitors.custom\_monitors.v1.CustomMonitorsService* `GET /api/monitors/custom-monitors/v1` | `SCOPE_MONITORS_READ` |
| `History` *synq.monitors.history.v1.HistoryService* `GET /api/monitors/history/v1` | `SCOPE_MONITORS_READ` `SCOPE_MCP_READ` |
| `MonitorInfo` *synq.monitors.info.v1.MonitorInfoService* `GET /api/monitors/info/v1` | `SCOPE_MONITORS_READ` `SCOPE_MCP_READ` |
| `BatchGetLatestEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `POST /api/monitors/predictions/v1/entity-metrics:batchLatest` | `SCOPE_MONITORS_READ` `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetEntityMetrics` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1/entity-metrics` | `SCOPE_MONITORS_READ` `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `GetMonitorPredictions` *synq.monitors.predictions.v1.MonitorPredictionsService* `GET /api/monitors/predictions/v1` | `SCOPE_MONITORS_READ` `SCOPE_MCP_READ` |
| `BatchUpdateMonitorSegmentLabels` *synq.monitors.segments.v1.MonitorSegmentsService* `POST /api/monitors/segments/v1/labels` | `SCOPE_MONITORS_CUSTOM_EDIT` `SCOPE_MCP_WRITE` |
| `ListMonitorSegments` *synq.monitors.segments.v1.MonitorSegmentsService* `GET /api/monitors/segments/v1` | `SCOPE_MONITORS_READ` `SCOPE_MCP_READ` |
| `BatchGetEntityOverlays` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/by-id` | `SCOPE_OWNERS_READ` `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `BatchGetOverlayMemberCounts` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/member-counts` | `SCOPE_OWNERS_READ` `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `GetCoverageSummary` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage-summary` | `SCOPE_OWNERS_READ` `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `ListEntityCoverage` *synq.overlays.v1.OverlaysService* `POST /api/overlays/v1/coverage` | `SCOPE_OWNERS_READ` `SCOPE_DATAPRODUCT_READ` `SCOPE_MCP_READ` |
| `BatchGetOwners` *synq.owners.v1.OwnersService* `POST /api/owners/v1/batch-get` | `SCOPE_OWNERS_READ` `SCOPE_MCP_READ` |
| `BatchGetOwnerships` *synq.owners.v1.OwnersService* `POST /api/owners/v1/ownerships/batch-get` | `SCOPE_OWNERSHIP_READ` `SCOPE_MCP_READ` |
| `DeleteOwner` *synq.owners.v1.OwnersService* `POST /api/owners/v1/delete` | `SCOPE_OWNERS_EDIT` `SCOPE_MCP_WRITE` |
| `DeleteOwnership` *synq.owners.v1.OwnersService* `POST /api/owners/v1/ownerships/delete` | `SCOPE_OWNERSHIP_EDIT` `SCOPE_MCP_WRITE` |
| `ListOwners` *synq.owners.v1.OwnersService* `GET /api/owners/v1` | `SCOPE_OWNERS_READ` `SCOPE_MCP_READ` |
| `ListOwnerships` *synq.owners.v1.OwnersService* `GET /api/owners/v1/{owner_id}/ownerships` | `SCOPE_OWNERSHIP_READ` `SCOPE_MCP_READ` |
| `UpsertOwner` *synq.owners.v1.OwnersService* `POST /api/owners/v1` | `SCOPE_OWNERS_EDIT` `SCOPE_MCP_WRITE` |
| `UpsertOwnership` *synq.owners.v1.OwnersService* `POST /api/owners/v1/{owner_id}/ownerships` | `SCOPE_OWNERSHIP_EDIT` `SCOPE_MCP_WRITE` |
| `SetPlatformAuthentication` *synq.platforms.v1.PlatformsService* `POST /api/platforms/v1` | `SCOPE_INTEGRATION_EDIT` |
| `GenerateQuery` *synq.queries.v1.NLQueryService* `POST /api/queries/v1/generate` | `SCOPE_ENTITY_READ` |
| `DescribeResolverQl` *synq.queries.v1.QueriesService* `POST /api/queries/v1/describe-resolver-ql` | — |
| `ResolveQuery` *synq.queries.v1.QueriesService* `POST /api/queries/v1/resolve` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `TranslateResolverQl` *synq.queries.v1.QueriesService* `POST /api/queries/v1/translate-resolver-ql` | `SCOPE_ENTITY_READ` `SCOPE_MCP_READ` |
| `BatchGetRecommendations` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/by-id` | `SCOPE_RECOMMENDATIONS_READ` `SCOPE_RECOMMENDATIONS_EDIT` |
| `DismissRecommendation` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/{recommendation_id}/dismiss` | `SCOPE_RECOMMENDATIONS_EDIT` |
| `IgnoreRecommendation` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/{recommendation_id}/ignore` | `SCOPE_RECOMMENDATIONS_EDIT` |
| `ListRecommendations` *synq.recommendations.v1.RecommendationsService* `GET /api/recommendations/v1` | `SCOPE_RECOMMENDATIONS_READ` `SCOPE_RECOMMENDATIONS_EDIT` |
| `RestoreRecommendation` *synq.recommendations.v1.RecommendationsService* `POST /api/recommendations/v1/{recommendation_id}/restore` | `SCOPE_RECOMMENDATIONS_EDIT` |
| `BatchGet` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/batch-get` | `SCOPE_SAVED_VIEW_READ` `SCOPE_MCP_READ` |
| `Delete` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/delete` | `SCOPE_SAVED_VIEW_EDIT` `SCOPE_SAVED_VIEW_ADMIN` `SCOPE_MCP_WRITE` |
| `List` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/list` | `SCOPE_SAVED_VIEW_READ` `SCOPE_MCP_READ` |
| `SetPin` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1/set-pin` | `SCOPE_SAVED_VIEW_READ` `SCOPE_MCP_READ` |
| `Upsert` *synq.savedviews.v1.SavedViewsService* `POST /api/saved-views/v1` | `SCOPE_SAVED_VIEW_EDIT` `SCOPE_SAVED_VIEW_ADMIN` `SCOPE_MCP_WRITE` |
| `PreviewSchedules` *synq.schedule.v1.ScheduleService* `POST /api/schedule/v1/preview` | — |
| `AddUserRole` *synq.users.v1.UsersService* `POST /api/users/v1/roles` | `SCOPE_USER_EDIT` |
| `BatchGetUsers` *synq.users.v1.UsersService* `POST /api/users/v1/batch-get` | `SCOPE_USER_READ` |
| `DeleteUserAliases` *synq.users.v1.UsersService* `POST /api/users/v1/aliases/delete` | `SCOPE_USER_EDIT` `SCOPE_PLATFORM_USER_LINK` |
| `GetByAlias` *synq.users.v1.UsersService* `POST /api/users/v1/by-alias` | `SCOPE_USER_READ` |
| `GetByEmail` *synq.users.v1.UsersService* `GET /api/users/v1/by-email` | `SCOPE_USER_READ` `SCOPE_MCP_READ` |
| `GetUserAliases` *synq.users.v1.UsersService* `GET /api/users/v1/aliases` | `SCOPE_USER_READ` |
| `GetUserIdentities` *synq.users.v1.UsersService* `GET /api/users/v1/identities` | `SCOPE_USER_READ` `SCOPE_MCP_READ` |
| `ListUsers` *synq.users.v1.UsersService* `GET /api/users/v1` | `SCOPE_USER_READ` `SCOPE_MCP_READ` |
| `RemoveUserRole` *synq.users.v1.UsersService* `POST /api/users/v1/roles/remove` | `SCOPE_USER_EDIT` |
| `UpsertUserAliases` *synq.users.v1.UsersService* `PUT /api/users/v1/aliases` | `SCOPE_USER_EDIT` `SCOPE_PLATFORM_USER_LINK` |
# TriggerDeployment
Source: https://docs.synq.io/api-reference/synqagentreconv1suitedeploymentservice/triggerdeployment
/api-reference/openapi.yaml post /api/recon/v1/deployments/trigger
Trigger an on-demand execution of a promoted deployment. Enqueues a run
using the frozen suite snapshot and connection mappings stored on the
deployment row — execution uses platform integration credentials, not the
caller's. Returns the freshly-allocated `run_id` so callers can poll
RunStateService for status.
Preconditions:
- Deployment must exist and be `active`.
- Deployment must have `triggerable_by_api = true` (FAILED_PRECONDITION
otherwise).
- Deployment must not be paused (FAILED_PRECONDITION when
`paused_until` is set and in the future).
# ParseYaml
Source: https://docs.synq.io/api-reference/synqagentreconv1yamlservice/parseyaml
/api-reference/openapi.yaml post /api/agent/recon/v1/yaml/parse
ParseYaml parses a YAML config string into a ReconciliationSuite proto.
Only the suite configuration fields are extracted (connections are ignored).
ParseYaml refuses two types of suite with INVALID_ARGUMENT and a
SuiteValidationFailure detail:
- A suite with `setup_file` or `teardown_file`. These fields refer to
files that the server cannot read.
- A suite with `reporting.level: detailed`.
To find all other problems in a suite, use ValidateSuite.
# RenderYaml
Source: https://docs.synq.io/api-reference/synqagentreconv1yamlservice/renderyaml
/api-reference/openapi.yaml post /api/agent/recon/v1/yaml/render
RenderYaml renders a ReconciliationSuite proto as a YAML config string.
# ValidateSuite
Source: https://docs.synq.io/api-reference/synqagentreconv1yamlservice/validatesuite
/api-reference/openapi.yaml post /api/agent/recon/v1/yaml/validate
ValidateSuite checks a suite and gives all the problems that it finds.
Each problem has the path of its field. ValidateSuite does not save the
suite, so an editor can call it after each change.
ValidateSuite does the same checks as `synq-recon check-config` without
`--db`. It also does the checks that apply only to suites that run in
Coalesce Quality.
ValidateSuite does not connect to a warehouse. Thus it does not find a
query that refers to a table that does not exist.
ValidateSuite does not compare the connection names with the integrations
of the workspace. A suite gets its integrations when it runs or when you
promote it.
# SubmitFeatureRequest
Source: https://docs.synq.io/api-reference/synqagentsrev1featurerequestservice/submitfeaturerequest
/api-reference/openapi.yaml post /api/feature-request/v1/submit
SubmitFeatureRequest submits a feature request to the Coalesce Quality product team.
This method should only be called as a last resort when the user's request cannot be
fulfilled by any existing tool. It logs the request for product team review.
# Evaluate
Source: https://docs.synq.io/api-reference/synqagentsrev1llmservice/evaluate
/api-reference/openapi.yaml post /api/sre/llm/v1/evaluate
Evaluates an LLM request with a structured output schema and message history.
The main prompt should be constant as it will be cached for efficiency.
# BatchLatestConclusions
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/batchlatestconclusions
/api-reference/openapi.yaml get /api/triage/v1/conclusions
Retrieves the latest triage state for one or more issues. This method is used by the LLM agent
to retrieve the current state of the issues it is working on.
# BatchTriageState
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/batchtriagestate
/api-reference/openapi.yaml get /api/triage/v1/states
Retrieves the triage state for one or more issues. This method is used by the LLM agent
to retrieve the current state of the issues it is working on.
# Conclude
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/conclude
/api-reference/openapi.yaml post /api/triage/v1/{session_id}/conclusion
Concludes the triage investigation for one or more issues by providing a structured conclusion
and supporting evidence. This method is used by the LLM agent to finalize its investigation.
# ListTriageState
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/listtriagestate
/api-reference/openapi.yaml get /api/triage/v1/statuses
Retrieves the triage state for one or more issues. This method is used by the LLM agent
to retrieve the current state of the issues it is working on.
# MarkAsInProgress
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/markasinprogress
/api-reference/openapi.yaml patch /api/triage/v1/in-progress
Marks an issue as in progress. This method is used by the LLM agent to indicate that it has started working on an issue.
# MarkFailed
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/markfailed
/api-reference/openapi.yaml patch /api/triage/v1/failed
Marks an issue as failed triaging. This method is used by the LLM agent to indicate that it was unable to triage.
# RecordEvidence
Source: https://docs.synq.io/api-reference/synqagentsrev1triageservice/recordevidence
/api-reference/openapi.yaml post /api/triage/v1/{session_id}/evidence
Records evidence collected during the triage investigation. This method allows the LLM agent
to incrementally add evidence as it discovers information during the investigation process.
# BatchGet
Source: https://docs.synq.io/api-reference/synqalertsservicesv1alertsservice/batchget
/api-reference/openapi.yaml post /api/alerts/v1/get
Get multiple alert configurations by their identifiers.
# Create
Source: https://docs.synq.io/api-reference/synqalertsservicesv1alertsservice/create
/api-reference/openapi.yaml post /api/alerts/v1
Create a new alert configuration.
# Delete
Source: https://docs.synq.io/api-reference/synqalertsservicesv1alertsservice/delete
/api-reference/openapi.yaml delete /api/alerts/v1
Delete an existing alert configuration.
The config can be identified by either ID or FQN.
# List
Source: https://docs.synq.io/api-reference/synqalertsservicesv1alertsservice/list
/api-reference/openapi.yaml post /api/alerts/v1/list
Retrieves a list of alert configurations based on filters.
# ToggleEnabled
Source: https://docs.synq.io/api-reference/synqalertsservicesv1alertsservice/toggleenabled
/api-reference/openapi.yaml post /api/alerts/v1/toggle
Enables or disables an existing alert configuration.
The config can be identified by either ID or FQN.
# Update
Source: https://docs.synq.io/api-reference/synqalertsservicesv1alertsservice/update
/api-reference/openapi.yaml put /api/alerts/v1
Update an existing alert configuration.
The config can be identified by either ID or FQN.
# BatchGet
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/batchget
/api-reference/openapi.yaml post /api/alerts/v2/get
Get multiple alert configurations by their identifiers.
# Create
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/create
/api-reference/openapi.yaml post /api/alerts/v2
Create a new alert configuration.
# Delete
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/delete
/api-reference/openapi.yaml delete /api/alerts/v2
Delete an existing alert configuration.
The config can be identified by either ID or FQN.
# GetIncidentOwnerAlert
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/getincidentowneralert
/api-reference/openapi.yaml get /api/alerts/v2/incident-owner
Reports whether the workspace's incident-owner alert is on. This alert has
no other configuration and does not appear in List or BatchGet; turn it on
or off with ToggleIncidentOwnerAlert.
# List
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/list
/api-reference/openapi.yaml post /api/alerts/v2/list
Retrieves a list of alert configurations based on filters.
# Rename
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/rename
/api-reference/openapi.yaml post /api/alerts/v2/rename
Rename an existing alert configuration.
The config can be identified by either ID or FQN.
# SendTestAlert
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/sendtestalert
/api-reference/openapi.yaml post /api/alerts/v2/test
Delivers a static test message to the given targets so you can confirm that
delivery reaches each channel before relying on a real alert. Nothing is
stored and the targets need not belong to a saved alert, so this can be
called while an alert is still being authored.
# ToggleEnabled
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/toggleenabled
/api-reference/openapi.yaml post /api/alerts/v2/toggle
Enables or disables an existing alert configuration.
The config can be identified by either ID or FQN.
# ToggleIncidentOwnerAlert
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/toggleincidentowneralert
/api-reference/openapi.yaml post /api/alerts/v2/incident-owner
Turns the workspace's incident-owner alert on or off. This alert notifies
each incident's owner on its configured channels and has no other
configuration, so it is managed here rather than through Create / Delete and
does not appear in List or BatchGet. Read its current state with
GetIncidentOwnerAlert.
# UpdateSettings
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/updatesettings
/api-reference/openapi.yaml post /api/alerts/v2/settings
Update the trigger and/or the kind-specific settings of an alert.
The config can be identified by either ID or FQN.
# UpdateTargets
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/updatetargets
/api-reference/openapi.yaml post /api/alerts/v2/targets
Replace the delivery targets of an existing alert configuration.
The config can be identified by either ID or FQN.
# UpdateTemplate
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/updatetemplate
/api-reference/openapi.yaml post /api/alerts/v2/template
Set or clear the message template of an existing alert configuration.
The config can be identified by either ID or FQN.
# Upsert
Source: https://docs.synq.io/api-reference/synqalertsservicesv2alertsservice/upsert
/api-reference/openapi.yaml put /api/alerts/v2
Creates the alert when no alert holds `fqn`, otherwise replaces the stored
alert with the state given here in a single write. Use it to make a stored
alert match a declared state, e.g. when deploying alerts from configuration
as code. See UpsertRequest for which fields cannot change.
# Iam
Source: https://docs.synq.io/api-reference/synqauthiamv1iamservice/iam
/api-reference/openapi.yaml get /api/iam/v1
Iam returns information about caller authentication
# BatchDeleteSqlTestDeploymentRules
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/batchdeletesqltestdeploymentrules
/api-reference/openapi.yaml delete /api/datachecks/sql-test-deployment-rules/v1/batch
# BatchDeploySqlTestDeploymentRules
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/batchdeploysqltestdeploymentrules
/api-reference/openapi.yaml post /api/datachecks/sql-test-deployment-rules/v1/batch/deployed
# BatchGetSqlTestDeploymentRules
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/batchgetsqltestdeploymentrules
/api-reference/openapi.yaml post /api/datachecks/sql-test-deployment-rules/v1/by-id
# DeleteSqlTestDeploymentRule
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/deletesqltestdeploymentrule
/api-reference/openapi.yaml delete /api/datachecks/sql-test-deployment-rules/v1/{id}
# DeploySqlTestDeploymentRule
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/deploysqltestdeploymentrule
/api-reference/openapi.yaml post /api/datachecks/sql-test-deployment-rules/v1/deployed
Deploy (create or update) a SqlTestDeploymentRule and immediately sync tests
onto its current match set.
# GetSqlTestDeployDeleteOverview
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/getsqltestdeploydeleteoverview
/api-reference/openapi.yaml get /api/datachecks/sql-test-deployment-rules/v1/{id}/deleted
Preview the effect of deleting a rule, without deleting it.
# GetSqlTestDeployOverview
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/getsqltestdeployoverview
/api-reference/openapi.yaml get /api/datachecks/sql-test-deployment-rules/v1/overview
Preview the effect of deploying a rule (which have not yet been saved), without
deploying it.
# ListSqlTestDeploymentRules
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsdeploymentrulesservice/listsqltestdeploymentrules
/api-reference/openapi.yaml get /api/datachecks/sql-test-deployment-rules/v1
# BatchDeleteSqlTests
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsservice/batchdeletesqltests
/api-reference/openapi.yaml delete /api/datachecks/sqltests/v1
Delete SqlTests by their IDs.
# BatchGetSqlTests
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsservice/batchgetsqltests
/api-reference/openapi.yaml post /api/datachecks/sqltests/v1/by-id
Get SqlTests by their IDs.
# BatchUpsertSqlTests
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsservice/batchupsertsqltests
/api-reference/openapi.yaml post /api/datachecks/sqltests/v1
Upsert SqlTests based on a unique ID.
# ListSqlTests
Source: https://docs.synq.io/api-reference/synqdatacheckssqltestsv1sqltestsservice/listsqltests
/api-reference/openapi.yaml get /api/datachecks/sqltests/v1
List SqlTests for given annotations.
# BatchAcceptTestSuggestions
Source: https://docs.synq.io/api-reference/synqdatacheckstestsuggestionsv1testsuggestionsservice/batchaccepttestsuggestions
/api-reference/openapi.yaml patch /api/datachecks/testsuggestions/v1/accepted
# BatchCreateTestSuggestions
Source: https://docs.synq.io/api-reference/synqdatacheckstestsuggestionsv1testsuggestionsservice/batchcreatetestsuggestions
/api-reference/openapi.yaml post /api/datachecks/testsuggestions/v1
# BatchGetTestSuggestionsForEntities
Source: https://docs.synq.io/api-reference/synqdatacheckstestsuggestionsv1testsuggestionsservice/batchgettestsuggestionsforentities
/api-reference/openapi.yaml get /api/datachecks/testsuggestions/v1
# BatchRejectTestSuggestions
Source: https://docs.synq.io/api-reference/synqdatacheckstestsuggestionsv1testsuggestionsservice/batchrejecttestsuggestions
/api-reference/openapi.yaml patch /api/datachecks/testsuggestions/v1/rejected
# TriggerDatachecks
Source: https://docs.synq.io/api-reference/synqdatachecksv1triggerservice/triggerdatachecks
/api-reference/openapi.yaml post /api/datachecks/scheduler/v1/trigger
TriggerDatachecks executes datachecks for the specified entities.
# Delete
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/delete
/api-reference/openapi.yaml post /api/dataproducts/v1/delete
Delete a dataproduct by its identifier.
Soft deletes by default. Optionally purge the dataproduct to release the identifier.
# Get
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/get
/api-reference/openapi.yaml post /api/dataproducts/v1/get
Get multiple dataproducts by their identifiers.
# List
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/list
/api-reference/openapi.yaml get /api/dataproducts/v1
List all the dataproducts in the current scope.
# RemoveDefinitionPart
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/removedefinitionpart
/api-reference/openapi.yaml post /api/dataproducts/v1/definition/part/delete
Remove definition part from the definition of an existing dataproduct.
# SetDefinition
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/setdefinition
/api-reference/openapi.yaml post /api/dataproducts/v1/definition
Set the definition for an existing dataproduct.
# Upsert
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/upsert
/api-reference/openapi.yaml post /api/dataproducts/v1
Create or update a dataproduct.
# UpsertDefinitionPart
Source: https://docs.synq.io/api-reference/synqdataproductsv1dataproductsservice/upsertdefinitionpart
/api-reference/openapi.yaml post /api/dataproducts/v1/definition/part
Upsert definition part to the definition of an existing dataproduct.
# BatchGet
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/batchget
/api-reference/openapi.yaml post /api/dataproducts/v2/batch-get
Fetch multiple data products by id. Ids that do not exist (or, unless
`include_deleted` is set, are deleted) are omitted from the response rather
than causing an error.
# Delete
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/delete
/api-reference/openapi.yaml post /api/dataproducts/v2/delete
Delete a data product. Idempotent — deleting an already-deleted or unknown
product succeeds. Soft-deletes by default; set `purge` to hard-delete and
release the id. Pass `etag` to guard against deleting a concurrently-edited
product.
# List
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/list
/api-reference/openapi.yaml get /api/dataproducts/v2
List the data products in the workspace, paginated. Narrow with `folder`
and `priorities`.
# ListMembers
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/listmembers
/api-reference/openapi.yaml get /api/dataproducts/v2/{id}/members
List the assets that currently belong to a data product, resolved from its
definition and returned as opaque entity ids. Paginated.
# RemoveDefinitionPart
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/removedefinitionpart
/api-reference/openapi.yaml delete /api/dataproducts/v2/{id}/definition/parts/{part_id}
Remove a single definition part by its part id. Idempotent.
# SetDefinition
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/setdefinition
/api-reference/openapi.yaml put /api/dataproducts/v2/{id}/definition
Replace the entire membership definition of a data product.
# Upsert
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/upsert
/api-reference/openapi.yaml post /api/dataproducts/v2
Create or update a data product. The id is supplied by the caller (a UUID);
repeating the same request converges to the same product. Mutable fields are
optional: a set field is written, an omitted field is left unchanged. Pass
`etag` to guard against overwriting a concurrent edit.
# UpsertDefinitionPart
Source: https://docs.synq.io/api-reference/synqdataproductsv2dataproductsservice/upsertdefinitionpart
/api-reference/openapi.yaml post /api/dataproducts/v2/{id}/definition/parts
Add or replace a single definition part (matched by its part id).
# BatchGetDeliveries
Source: https://docs.synq.io/api-reference/synqdeliveriesv1deliveriesservice/batchgetdeliveries
/api-reference/openapi.yaml post /api/deliveries/v1/by-id
Fetch deliveries by id. Ids that do not exist, or have passed out of the
retention window, are omitted from the response rather than causing an
error. Unlike `ListDeliveries`, this always returns the routing provenance.
# ListAttempts
Source: https://docs.synq.io/api-reference/synqdeliveriesv1deliveriesservice/listattempts
/api-reference/openapi.yaml get /api/deliveries/v1/{delivery_id}/attempts
List every attempt made for one delivery, oldest first. A delivery is
attempted at most ten times, so the result is small and is not paginated.
# ListDeliveries
Source: https://docs.synq.io/api-reference/synqdeliveriesv1deliveriesservice/listdeliveries
/api-reference/openapi.yaml get /api/deliveries/v1
List deliveries, newest event first. With no `integration_id` this is the
whole workspace's feed; with one it is that integration's.
# Redeliver
Source: https://docs.synq.io/api-reference/synqdeliveriesv1deliveriesservice/redeliver
/api-reference/openapi.yaml post /api/deliveries/v1/{delivery_id}/redeliver
Send an earlier delivery again, to the same integration, with the same
event. The replay is a new delivery with its own id, linked to the original
through `redelivery_of`, and appears in `ListDeliveries` like any other.
It is signed with the integration's current secret rather than the one the
original used, which makes it the way to confirm that a rotated secret is
accepted by the receiver.
Retrying this call sends the delivery again. Rate-limited per integration;
over the limit it fails with `RESOURCE_EXHAUSTED` rather than queueing.
# SendTestDelivery
Source: https://docs.synq.io/api-reference/synqdeliveriesv1deliveriesservice/sendtestdelivery
/api-reference/openapi.yaml post /api/deliveries/v1/test
Send a test delivery to an integration, to confirm the endpoint is reachable
and that it accepts what is sent. It is always sent, whatever event types
the integration subscribes to — a test that silently matches nothing would
report the same as a broken endpoint.
Returns the id of the delivery it created. The send is asynchronous: poll
`BatchGetDeliveries` with that id until the outcome leaves `OUTCOME_PENDING`.
Retrying this call sends another test. Rate-limited per integration; over
the limit it fails with `RESOURCE_EXHAUSTED` rather than queueing.
# SummariseDeliveries
Source: https://docs.synq.io/api-reference/synqdeliveriesv1deliveriesservice/summarisedeliveries
/api-reference/openapi.yaml post /api/deliveries/v1/summarise
Count deliveries over a window, bucketed by outcome, by skip reason and by
HTTP status class. Use it to watch your own delivery failure rate without
paging through the feed.
# AddDomainMembers
Source: https://docs.synq.io/api-reference/synqdomainsmembersv1domainmembersservice/adddomainmembers
/api-reference/openapi.yaml post /api/domains/members/v1/{domain_id}/members
Add domain members.
# ListDomainMembers
Source: https://docs.synq.io/api-reference/synqdomainsmembersv1domainmembersservice/listdomainmembers
/api-reference/openapi.yaml get /api/domains/members/v1/{domain_id}/members
List domain members.
# ListUserDomains
Source: https://docs.synq.io/api-reference/synqdomainsmembersv1domainmembersservice/listuserdomains
/api-reference/openapi.yaml get /api/domains/members/v1/users/{user_id}/domains
List domains for the given user.
# RemoveDomainMembers
Source: https://docs.synq.io/api-reference/synqdomainsmembersv1domainmembersservice/removedomainmembers
/api-reference/openapi.yaml post /api/domains/members/v1/{domain_id}/members/remove
Remove domain members.
# SetDefaultUserDomain
Source: https://docs.synq.io/api-reference/synqdomainsmembersv1domainmembersservice/setdefaultuserdomain
/api-reference/openapi.yaml post /api/domains/members/v1/{domain_id}/default-for-user
Set default domain for user.
# BatchGet
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/batchget
/api-reference/openapi.yaml post /api/domains/v1/batch-get
Get multiple domains by their identifiers.
# Delete
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/delete
/api-reference/openapi.yaml post /api/domains/v1/delete
Delete a domain by its identifier.
# FetchDomainWithSubDomains
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/fetchdomainwithsubdomains
/api-reference/openapi.yaml get /api/domains/v1/{domain_id}/sub-domains
Get a domain with its sub-domains
# GetUsage
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/getusage
/api-reference/openapi.yaml get /api/domains/v1/usage
Report how many domains the workspace uses, split into top-level and sub-domains.
# List
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/list
/api-reference/openapi.yaml get /api/domains/v1
List all the domains in the current workspace.
# RemoveDefinitionPart
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/removedefinitionpart
/api-reference/openapi.yaml delete /api/domains/v1/{domain_id}/definition/parts/{part_id}
Remove definition part from the definition of an existing domain.
# SetDefinition
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/setdefinition
/api-reference/openapi.yaml put /api/domains/v1/{domain_id}/definition
Set the definition for an existing domain.
# Upsert
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/upsert
/api-reference/openapi.yaml post /api/domains/v1
Create or update a domain.
# UpsertDefinitionPart
Source: https://docs.synq.io/api-reference/synqdomainsv1domainsservice/upsertdefinitionpart
/api-reference/openapi.yaml post /api/domains/v1/{domain_id}/definition/parts
Upsert definition part to the definition of an existing domain.
# BatchGet
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/batchget
/api-reference/openapi.yaml post /api/domains/v2/batch-get
Fetch multiple domains by id. Ids that do not exist (or, unless
`include_deleted` is set, are deleted) are omitted from the response rather
than causing an error.
# Delete
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/delete
/api-reference/openapi.yaml post /api/domains/v2/delete
Delete a domain. Idempotent — deleting an already-deleted or unknown domain
succeeds. Soft-deletes by default; set `purge` to hard-delete and release the
id, which is refused while the domain still has sub-domains. Pass `etag` to
guard against deleting a concurrently-edited domain.
# FetchDomainWithSubDomains
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/fetchdomainwithsubdomains
/api-reference/openapi.yaml get /api/domains/v2/{id}/sub-domains
Fetch a domain together with its direct sub-domains, keyed by id.
# GetUsage
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/getusage
/api-reference/openapi.yaml get /api/domains/v2/usage
Report how many domains the workspace uses, split into top-level and sub-domains.
# List
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/list
/api-reference/openapi.yaml get /api/domains/v2
List the domains in the workspace, paginated. Narrow to one level of the
hierarchy with `parent_id`.
# ListMembers
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/listmembers
/api-reference/openapi.yaml get /api/domains/v2/{id}/members
List the assets that currently belong to a domain, resolved from its
definition — including everything contributed by the data products and
domains it references, and by its own sub-domains — and returned as opaque
entity ids. Paginated.
# RemoveDefinitionPart
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/removedefinitionpart
/api-reference/openapi.yaml delete /api/domains/v2/{id}/definition/parts/{part_id}
Remove a single definition part by its part id. Idempotent.
# SetDefinition
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/setdefinition
/api-reference/openapi.yaml put /api/domains/v2/{id}/definition
Replace the entire membership definition of a domain.
# Upsert
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/upsert
/api-reference/openapi.yaml post /api/domains/v2
Create or update a domain. The id is supplied by the caller (a UUID);
repeating the same request converges to the same domain. Mutable fields are
optional: a set field is written, an omitted field is left unchanged. Pass
`etag` to guard against overwriting a concurrent edit.
# UpsertDefinitionPart
Source: https://docs.synq.io/api-reference/synqdomainsv2domainsservice/upsertdefinitionpart
/api-reference/openapi.yaml post /api/domains/v2/{id}/definition/parts
Add or replace a single definition part (matched by its part id).
# BatchGetEntityUsage
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/batchgetentityusage
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/usage
Read how each asked entity is used over the window, in one line: what loads it, what runs and
builds it, what else writes it, and who reads it, such as "loaded by Fivetran, orchestrated by
Airflow, transformed by dbt, read by Looker Studio, 3 people and 12 logins". Each role names its actor at
the most precise level known: a job or app, else a service account, else the tool. It answers
"what runs this" for a table or a model, and GetActorSummary answers "what this job runs". It
names tools, jobs and service accounts, never a login.
# BatchGetPopularity
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/batchgetpopularity
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/popularity
Read how popular each asked entity is over the window: its counted reads and writes per measure,
and its rank in each pool it has reads in. An entity is answered with the entities it is the same
as, such as a dbt model and the table it builds. Every asked id has an answer, an empty one when
nothing counted.
# GetActivityOverview
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/getactivityoverview
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/overview
Read a workspace's queries and their cost per group of logins, per platform, and its most read
entities. It names groups and entities, never a login or a person.
# GetActorSummary
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/getactorsummary
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/actor-summary
Read what one actor read, built and wrote over the window, what its queries cost per compute and
platform, and when it works. The actor is asked for by name, so nothing is left out for being
Coalesce Quality's own access, a writer's own read or a policy.
What a person did is personal data: a person, or a login, is answered only to that person, or to a
caller holding the platform users scope, which a Developer and an Admin have. Anyone else gets
PERMISSION_DENIED. A job, a service account, a tool and Coalesce Quality are answered to every
caller, and without the platform users scope the answer names no login.
# GetEntityActivitySeries
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/getentityactivityseries
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/series
Read how access to one entity moves over the window: per direction, a point per UTC hour, a point
per day in the asked time zone, and a weekday and hour grid. A direction's total is the sum of its
actors in ListEntityActors under the same lens. It names tools, never a login.
# ListAccessSignals
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/listaccesssignals
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/signals
List the entities of the workspace, or of a selection, that one signal names, such as the tables
a pipeline builds and nobody reads, or the tables nobody touched. A signal is computed from the
window's access on each call, so a service account change shows on the next one. It names
entities and tools, never a login.
# ListActors
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/listactors
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/actor-list
List the workspace's actors, the service accounts, tools, people, logins nothing classifies and
Coalesce Quality, each with the totals GetActorSummary has for it: its queries and their weights,
its reads, inputs and writes, and the entities it read and wrote. A login can be in several actors,
such as its service account and the tool that issued its queries, so the actors never add up to
the workspace: GetActivityOverview is the split that does. Only a caller holding the platform users
scope sees people and logins: any other gets the rest with people_hidden set.
# ListEntityActors
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/listentityactors
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/actors
Read who read and wrote each asked entity over the window: the jobs, the service accounts, the
tools and the logins. Anyone who reads lineage, and the reliability agent, is served. Only a caller holding the platform users
scope sees the logins: any other gets every logins list empty and logins_hidden set.
# ListPersonActivity
Source: https://docs.synq.io/api-reference/synqentitiesactivityv1alphaactivityservice/listpersonactivity
/api-reference/openapi.yaml post /api/entities/activity/v1alpha/person
Read what one person's logins read and wrote over the window, per entity, direction and measure.
What a colleague read is personal data, so a user caller needs the platform users scope from its
role, which a Developer and an Admin have. Logins in a service account, and Coalesce Quality's
own queries, are never part of the answer.
# BatchGetEntityAnnotations
Source: https://docs.synq.io/api-reference/synqentitiesannotationsv1annotationsservice/batchgetentityannotations
/api-reference/openapi.yaml post /api/entities/annotations/v1/batchGet
Read the annotations carried by specific entities.
ListAnnotations answers what the workspace annotates by and how often;
this answers what one entity carries. They are separate reads because an
entity's annotations are not part of the entity: `synq.entities.v1.Entity`
is the shape `synq.entities.custom.v1.UpsertEntity` accepts, so the
`annotations` field on it is what a producer ASSERTS about an entity, and
answering a read with it would make a read-edit-write round trip write one
entity's annotations onto another.
# ListAnnotations
Source: https://docs.synq.io/api-reference/synqentitiesannotationsv1annotationsservice/listannotations
/api-reference/openapi.yaml get /api/entities/annotations/v1
List all available annotations with their usage counts across entities.
# BatchChange
Source: https://docs.synq.io/api-reference/synqentitieschangesv1changesservice/batchchange
/api-reference/openapi.yaml post /api/changes/v1/by-id
BatchChange retrieves multiple entity changes by their IDs in a single request.
This is an optimized endpoint for fetching specific changes when you already know their IDs,
rather than listing changes for an entity.
# List
Source: https://docs.synq.io/api-reference/synqentitieschangesv1changesservice/list
/api-reference/openapi.yaml get /api/changes/v1
Retrieves the complete change history for specified entities.
Returns all change types (git commits, schema changes, SQL changes) in chronological order (newest first).
Changes include lineage context showing upstream_distance when changes affect dependencies.
# DeleteCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv1checkscategoriesservice/deletecheckcategories
/api-reference/openapi.yaml post /api/entities/checks/v1/categories:delete
DeleteCheckCategories clears the explicit category (both dimensions)
for the given checks. Checks fall back to their rule-computed
categories afterwards.
# ListCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv1checkscategoriesservice/listcheckcategories
/api-reference/openapi.yaml get /api/entities/checks/v1/categories
ListCheckCategories returns every explicit check category in the
caller's workspace.
# UpsertCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv1checkscategoriesservice/upsertcheckcategories
/api-reference/openapi.yaml post /api/entities/checks/v1/categories:upsert
UpsertCheckCategories sets (or replaces) the explicit category for
one or more checks. Each entry is the complete state for its check —
both dimensions — so an empty `category` / `governance_category` means
"no explicit value for that dimension".
# BatchGetCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategoriesservice/batchgetcheckcategories
/api-reference/openapi.yaml post /api/entities/v2/checks/categories/batch-get
Read the categories of specific checks. Ids that do not exist are omitted
from the response rather than causing an error, so a partially-stale list
of ids still returns what it can.
# DeleteCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategoriesservice/deletecheckcategories
/api-reference/openapi.yaml post /api/entities/v2/checks/categories/delete
Clear the explicit categories of one or more checks, on both dimensions.
Each check falls back to its producer-declared or rule-computed category.
Idempotent — clearing a check that has no explicit category succeeds.
# ListCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategoriesservice/listcategories
/api-reference/openapi.yaml post /api/entities/v2/checks/categories/vocabulary
List the workspace's category vocabulary: the category names in use on
each dimension, whether a workspace or platform rule produces each one, and
how many checks carry it. Use it to populate a category picker so a
workspace converges on one spelling per concept, and to see how much of the
workspace is still uncategorised.
# ListCategorisationFilters
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategoriesservice/listcategorisationfilters
/api-reference/openapi.yaml post /api/entities/v2/checks/categories/filters
List the attribute values the workspace's checks actually have — the
platforms, entity types, packages and kinds available to build a
categorisation rule or a listing filter from.
Pass `selected` to narrow each list to values that co-occur with the
current selection, so a rule editor never offers a combination that matches
nothing. A list never constrains itself: choosing one platform still leaves
every other platform selectable.
# ListCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategoriesservice/listcheckcategories
/api-reference/openapi.yaml post /api/entities/v2/checks/categories/list
List the workspace's checks with their resolved categories. Returns every
check by default; the filters combine, so one call answers questions like
"which dbt tests still have no governance category" or "which checks did
this rule categorise".
# UpsertCheckCategories
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategoriesservice/upsertcheckcategories
/api-reference/openapi.yaml post /api/entities/v2/checks/categories
Set the category of one or more checks explicitly. An explicit category is
authoritative for its check: it overrides both the producer-declared
category and anything the categorisation rules compute, and it survives
every later rule change.
Each dimension updates independently. A field that is omitted leaves that
dimension as it is; a field set to the empty string clears the explicit
category on that dimension, so the check falls back to its
producer-declared or rule-computed value. Repeating the same request
converges to the same state rather than accumulating changes.
# BatchGetRules
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/batchgetrules
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules/batch-get
Read specific rules by id — typically to resolve the `rule_id` a
ResolvedCategory carries into the rule that produced it. Ids the caller
cannot see, or that do not exist, are omitted from the response rather than
causing an error.
# DeleteRule
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/deleterule
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules/delete
Delete a categorisation rule. Checks the rule categorised fall back to the
next rule that matches them, or become uncategorised. Idempotent —
deleting an already-deleted or unknown rule succeeds. Pass `etag` to guard
against deleting a concurrently-edited rule.
# GetRecategorisationStatus
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/getrecategorisationstatus
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules/recategorisation-status
Progress of the re-categorisation sweep a rule change schedules. The sweep
normally completes in seconds, so this is how to confirm that the
workspace's existing checks have caught up with the new rules — not
something a client has to wait on.
# ListRuleMatches
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/listrulematches
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules/matches
Report how many checks each rule currently categorises, with a sample of
them — the state already applied, not a projection. Use it to show
"categorises N checks" next to each rule, and to spot a rule that has
stopped matching anything. For the full, paginated list of one rule's
checks, call ChecksCategoriesService.ListCheckCategories with the rule id.
# ListRules
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/listrules
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules/list
List the rules that apply to the caller's workspace — its own rules plus
the platform-provided ones — in the order they are tried. Narrow with
`dimension` and `scope`.
# PreviewRule
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/previewrule
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules/preview
Report what a candidate rule would change, without saving it.
The candidate is slotted into the workspace's live rule set — replacing an
existing rule when `id` names one — and the whole resolution is run over
every check, so scope, priority, explicit categories and producer-declared
categories are all honoured. The response separates the candidate's raw
reach from its real effect, which are rarely the same number.
Preview a rule before saving it: one that matches broadly can recategorise
a large part of a workspace in a single sweep.
# UpsertRule
Source: https://docs.synq.io/api-reference/synqentitieschecksv2checkscategorisationrulesservice/upsertrule
/api-reference/openapi.yaml post /api/entities/v2/checks/categorisation-rules
Create or update a categorisation rule.
The rule id is supplied by the caller (a UUID), which makes this operation
idempotent — repeating the same request converges to the same rule rather
than creating a duplicate. On create, `dimension`, `title`, `category` and
`matcher` are required. On update, a field that is set is written and a
field that is omitted is left unchanged. To guard against overwriting a
concurrent edit, pass the `etag` you last read.
Saving a rule schedules a sweep that recategorises the workspace's existing
checks. It normally completes in seconds; GetRecategorisationStatus
confirms it.
# ListCode
Source: https://docs.synq.io/api-reference/synqentitiescodev1codeservice/listcode
/api-reference/openapi.yaml get /api/code/v1
ListCode retrieves all code artifacts associated with a specific entity.
This includes SQL queries, Python scripts, dbt models, and other code configurations
that define or are part of the entity's functionality.
# GetTableConstraints
Source: https://docs.synq.io/api-reference/synqentitiesconstraintsv1tableconstraintsservice/gettableconstraints
/api-reference/openapi.yaml get /api/constraints/v1/table-constraints
Get table constraints (primary keys, foreign keys, indexes, etc.) for a given entity.
# BatchDatabaseCoordinates
Source: https://docs.synq.io/api-reference/synqentitiescoordinatesv1databasecoordinatesservice/batchdatabasecoordinates
/api-reference/openapi.yaml post /api/coordinates/v1/database/by-id
Get multiple DatabaseCoordinates for identifiers.
# BatchIdsByCoordinates
Source: https://docs.synq.io/api-reference/synqentitiescoordinatesv1databasecoordinatesservice/batchidsbycoordinates
/api-reference/openapi.yaml post /api/coordinates/v1/database/by-fqn
Given database FQN return possible entity ids.
# DeleteCheckRelationships
Source: https://docs.synq.io/api-reference/synqentitiescustomv1checksrelationshipsservice/deletecheckrelationships
/api-reference/openapi.yaml delete /api/entities/custom/v1/check-relationships
Delete relationships between checks and the entities they validate.
Relationships that do not exist are ignored.
The response says which of them actually removed something.
# ListCheckRelationships
Source: https://docs.synq.io/api-reference/synqentitiescustomv1checksrelationshipsservice/listcheckrelationships
/api-reference/openapi.yaml post /api/entities/custom/v1/check-relationships/list
List every relationship that attaches a check to the entities it
validates, across the whole workspace.
# UpsertCheckRelationships
Source: https://docs.synq.io/api-reference/synqentitiescustomv1checksrelationshipsservice/upsertcheckrelationships
/api-reference/openapi.yaml post /api/entities/custom/v1/check-relationships
Create or update relationships that attach a check (e.g. a SQL test) to the
entities it validates. Idempotent per (check, checked) pair: an existing
relationship is updated, otherwise it is created.
The response reports what each requested relationship did, and under which
(check, checked) pair of entities it was stored.
# DeleteEntity
Source: https://docs.synq.io/api-reference/synqentitiescustomv1entitiesservice/deleteentity
/api-reference/openapi.yaml delete /api/entities/custom/v1/entities
Delete an entity by its identifier. If the entity does not exist, it will be a no-op.
# ListEntities
Source: https://docs.synq.io/api-reference/synqentitiescustomv1entitiesservice/listentities
/api-reference/openapi.yaml get /api/entities/custom/v1/entities
List all entities.
# UpsertEntity
Source: https://docs.synq.io/api-reference/synqentitiescustomv1entitiesservice/upsertentity
/api-reference/openapi.yaml post /api/entities/custom/v1/entities
Create or update an entity. If the entity does not exist, it will be created, otherwise it will be updated. Entities are identified and deduplicated by their Identifier in a scope of a given customer workspace.
# UpsertExecution
Source: https://docs.synq.io/api-reference/synqentitiescustomv1entityexecutionsservice/upsertexecution
/api-reference/openapi.yaml post /api/entities/custom/executions/v1
Execution affects status of the custom entity
# UpsertLogEntry
Source: https://docs.synq.io/api-reference/synqentitiescustomv1entityexecutionsservice/upsertlogentry
/api-reference/openapi.yaml post /api/entities/custom/executions/v1/log-entry
LogEntry is a log message for the custom entity
# DeleteEntityFeature
Source: https://docs.synq.io/api-reference/synqentitiescustomv1featuresservice/deleteentityfeature
/api-reference/openapi.yaml delete /api/entities/custom/v1/features
Delete an entity by its identifier. If the entity does not exist, it will be a no-op.
# ListEntityFeatures
Source: https://docs.synq.io/api-reference/synqentitiescustomv1featuresservice/listentityfeatures
/api-reference/openapi.yaml post /api/entities/custom/v1/features/list
List the features of one entity, or of every custom entity in the
workspace when entity_id is left unset.
# UpsertEntityFeature
Source: https://docs.synq.io/api-reference/synqentitiescustomv1featuresservice/upsertentityfeature
/api-reference/openapi.yaml post /api/entities/custom/v1/features
Create or update an entity. If the entity does not exist, it will be created, otherwise it will be updated. Entities are identified and deduplicated by their Identifier in a scope of a given customer workspace.
# DeleteEntitiesGroup
Source: https://docs.synq.io/api-reference/synqentitiescustomv1groupsservice/deleteentitiesgroup
/api-reference/openapi.yaml delete /api/entities/custom/v1/groups
DeleteEntitiesGroup deletes a group by its identifier. If the group does not exist, it will be a no-op.
# ListEntityGroups
Source: https://docs.synq.io/api-reference/synqentitiescustomv1groupsservice/listentitygroups
/api-reference/openapi.yaml post /api/entities/custom/v1/groups/list
ListEntityGroups lists all groups.
# UpsertEntitiesGroup
Source: https://docs.synq.io/api-reference/synqentitiescustomv1groupsservice/upsertentitiesgroup
/api-reference/openapi.yaml post /api/entities/custom/v1/groups
UpsertEntitiesGroup updates current definition of group to the server. As part of the upsert operation
the difference of entity ids is calculated between past state and the current state and entities that are
no longer part of the group are removed.
# DeleteRelationships
Source: https://docs.synq.io/api-reference/synqentitiescustomv1relationshipsservice/deleterelationships
/api-reference/openapi.yaml delete /api/entities/custom/v1/relationships
Delete relationships between entities. If the relationship does not exist, it will be ignored.
The response says which of them actually removed something, so a caller
reconciling a set of edges can tell a withdrawal from a no-op.
# ListRelationships
Source: https://docs.synq.io/api-reference/synqentitiescustomv1relationshipsservice/listrelationships
/api-reference/openapi.yaml post /api/entities/custom/v1/relationships/list
List relationships touching the given entities, or every relationship in
the workspace when ids is left empty.
# UpsertRelationships
Source: https://docs.synq.io/api-reference/synqentitiescustomv1relationshipsservice/upsertrelationships
/api-reference/openapi.yaml post /api/entities/custom/v1/relationships
Create or update relationships between entities. If the relationship already exists, it will be updated, otherwise it will be created.
The response reports what each requested relationship did to the stored
graph, and under which pair of entities it was stored. Read it: a
relationship whose endpoints resolve to a pair another relationship in the
same batch also resolves to is one edge, and only the last write to it
survives.
# DeleteType
Source: https://docs.synq.io/api-reference/synqentitiescustomv1typesservice/deletetype
/api-reference/openapi.yaml delete /api/entities/custom/v1/types
Delete an entity by its identifier. If the entity does not exist, it will be a no-op.
# ListTypes
Source: https://docs.synq.io/api-reference/synqentitiescustomv1typesservice/listtypes
/api-reference/openapi.yaml post /api/entities/custom/v1/types/list
List all entities.
# UpsertType
Source: https://docs.synq.io/api-reference/synqentitiescustomv1typesservice/upserttype
/api-reference/openapi.yaml post /api/entities/custom/v1/types
Create or update an entity. If the entity does not exist, it will be created, otherwise it will be updated. Entities are identified and deduplicated by their Identifier in a scope of a given customer workspace.
# BatchGetEntities
Source: https://docs.synq.io/api-reference/synqentitiesentitiesv1entitiesservice/batchgetentities
/api-reference/openapi.yaml post /api/entities/v1/by-id
Get multiple entities by their identifiers.
# ListEntities
Source: https://docs.synq.io/api-reference/synqentitiesentitiesv1entitiesservice/listentities
/api-reference/openapi.yaml get /api/entities/v1
List entities matching the given filter
# SearchEntities
Source: https://docs.synq.io/api-reference/synqentitiesentitiesv1entitiesservice/searchentities
/api-reference/openapi.yaml get /api/entities/v1/search
Search for entities.
# UpsertExecution
Source: https://docs.synq.io/api-reference/synqentitiesexecutionsv1entityexecutionsservice/upsertexecution
/api-reference/openapi.yaml post /api/executions/v1
Execution affects status of the custom entity
# UpsertLogEntry
Source: https://docs.synq.io/api-reference/synqentitiesexecutionsv1entityexecutionsservice/upsertlogentry
/api-reference/openapi.yaml post /api/executions/v1/log-entry
LogEntry is a log message for the custom entity
# BatchGetExecutions
Source: https://docs.synq.io/api-reference/synqentitiesexecutionsv2entityexecutionsservice/batchgetexecutions
/api-reference/openapi.yaml post /api/executions/v2/by-id
BatchGetExecutions retrieves multiple executions by their IDs.
This is useful when you already know the execution IDs and want to fetch their details.
# GetExecutionsSummary
Source: https://docs.synq.io/api-reference/synqentitiesexecutionsv2entityexecutionsservice/getexecutionssummary
/api-reference/openapi.yaml post /api/executions/v2/summary
GetExecutionsSummary provides aggregated statistics about executions for specified entities.
This is useful for quickly understanding execution patterns without fetching all execution details.
Returns:
- Counts by execution type
- Counts by status
- Time range of available executions
- Latest execution per entity
# GetLatestExecutions
Source: https://docs.synq.io/api-reference/synqentitiesexecutionsv2entityexecutionsservice/getlatestexecutions
/api-reference/openapi.yaml post /api/executions/v2/latest
GetLatestExecutions retrieves the most recent execution for each specified entity.
IMPORTANT: Only returns executions where the entity appears in EntityExecution.entities (what executed).
This means it returns executions OF the entity (e.g., model runs, monitor runs, task runs),
NOT executions that merely reference the entity in targets or extra_references.
For example, querying for a table won't return results because tables don't execute on their own -
they are created/updated by models (which do execute).
Optionally filter by execution status to get the latest execution with a specific status.
# ListExecutions
Source: https://docs.synq.io/api-reference/synqentitiesexecutionsv2entityexecutionsservice/listexecutions
/api-reference/openapi.yaml get /api/executions/v2/list
ListExecutions retrieves a paginated list of executions for one or more entities.
Executions are returned in reverse chronological order (newest first).
Supports filtering by:
- Entity identifiers (required)
- Time range
- Execution type(s)
- Status(es)
# Browse
Source: https://docs.synq.io/api-reference/synqentitiesfoldersv1foldersservice/browse
/api-reference/openapi.yaml get /api/entities/folders/v1/browse
Browse the hierarchy one level at a time. With no `parent`, returns the
roots (top-level integrations / containers). With a `parent` folder, returns
that folder's direct sub-folders and the entity ids of its direct,
non-folder members.
One level per call — sub-folders are returned as `Folder`s (each with a
`child_folder_count`), which you Browse into in turn; there is no nested
tree in the response. `filter` narrows the returned members to entities
matching a query.
# GetFolderOf
Source: https://docs.synq.io/api-reference/synqentitiesfoldersv1foldersservice/getfolderof
/api-reference/openapi.yaml get /api/entities/folders/v1/folder-of
Given entity ids, return the folder breadcrumb(s) each entity lives in —
ordered root first, leaf (immediate parent) last. An entity may appear in
more than one hierarchy; the response carries every folder along its
containing chain. Compose with `ListMembers` on an entity's immediate parent
to list its siblings.
# ListMembers
Source: https://docs.synq.io/api-reference/synqentitiesfoldersv1foldersservice/listmembers
/api-reference/openapi.yaml get /api/entities/folders/v1/members
List the entity ids contained in a folder, paginated. Direct members only by
default; set `deep = true` to include members of all nested sub-folders.
Narrow with `types` and/or `filter`.
Pagination is a stable client-side window over the folder's fully-resolved
member set: each page recomputes and re-orders that set, then returns the
requested slice. Results are consistent, but paging deep through a very
large folder repeats work — prefer a larger `page_size` over many small
pages.
# GetImpact
Source: https://docs.synq.io/api-reference/synqentitiesimpactv1impactservice/getimpact
/api-reference/openapi.yaml post /api/impact/v1
# BatchGetFoldNodes
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/batchgetfoldnodes
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/nodes/batch-get
Read fold nodes by entity id: which fold a node in a lineage response
stands for, and which entities it showed. An id is answered for as long as
lineage may still return it; an id with nothing to answer is absent from
the response.
# BatchGetFolds
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/batchgetfolds
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/batch-get
Read folds by id. An id with no fold is absent from the response.
# CreateFold
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/createfold
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha
Create a fold. Repeating the same request returns the fold it created;
creating a different fold under an id already in use is refused with
ALREADY_EXISTS.
# DeleteFold
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/deletefold
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/{id}/delete
Delete a fold. Deleting a fold that does not exist succeeds.
# ListFolds
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/listfolds
/api-reference/openapi.yaml get /api/lineage/folds/v1alpha
List the ids of every fold in the workspace, switched off ones included,
oldest first. Read the folds with `BatchGetFolds`.
# PreviewFold
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/previewfold
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/preview
Show what a matcher would fold, before it is saved: how many entities and
lineage edges it matches, a sample of them, and the entities that stop
being shown on the same node as a folded one. Answered from the lineage the
workspace has now, without any fold applied.
# RenameFold
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/renamefold
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/{id}/rename
Change the name shown on a fold's node.
# SetFoldEnabled
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/setfoldenabled
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/{id}/enabled
Switch a fold on or off. A fold switched off is kept and listed, and
lineage shows the entities it matched as their own nodes again.
# UpdateFoldMatcher
Source: https://docs.synq.io/api-reference/synqentitieslineagefoldsv1alphalineagefoldsservice/updatefoldmatcher
/api-reference/openapi.yaml post /api/lineage/folds/v1alpha/{id}/matcher
Change which entities a fold matches. The fold's node keeps its entity id.
# GetLineage
Source: https://docs.synq.io/api-reference/synqentitieslineagev1lineageservice/getlineage
/api-reference/openapi.yaml post /api/lineage/v1
# BatchOrchestration
Source: https://docs.synq.io/api-reference/synqentitiesorchestrationv1orchestrationservice/batchorchestration
/api-reference/openapi.yaml get /api/orchestration/v1
BatchOrchestration returns, for each requested entity, the jobs, job steps and tasks that ran it
in the last 31 days (upstream), and the entities it ran (downstream).
# BatchGetPlatformUsers
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaplatformusersservice/batchgetplatformusers
/api-reference/openapi.yaml post /api/entities/platform-users/v1alpha/batch-get
Read logins by platform user id. Ids no integration declared and with no activity in the window
are left out.
# ListLinkSuggestions
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaplatformusersservice/listlinksuggestions
/api-reference/openapi.yaml post /api/entities/platform-users/v1alpha/link-suggestions
Suggest which workspace member each unclassified login may belong to, by matching the login
against the members the caller passes. Accept a suggestion with synq.users.v1
UsersService.UpsertUserAliases. A login already linked is skipped only when the caller lists its
platform_user_id among a member's identities.
# ListPlatformUsers
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaplatformusersservice/listplatformusers
/api-reference/openapi.yaml post /api/entities/platform-users/v1alpha/list
List the workspace's logins by their share of the workspace's activity, highest first. The first
page carries a summary of the whole window.
# ApplyServiceAccountChanges
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaserviceaccountsservice/applyserviceaccountchanges
/api-reference/openapi.yaml post /api/entities/service-accounts/v1alpha/apply
Write a planned change set, all of it or nothing. It is refused with FAILED_PRECONDITION when the
token is not from a plan of exactly this change set against the workspace as it is now: plan it
again. A change to an account or person link whose etag no longer matches is refused with
ABORTED, and nothing is written. Caller-supplied ids and etags make a retry converge.
# BatchGetServiceAccounts
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaserviceaccountsservice/batchgetserviceaccounts
/api-reference/openapi.yaml post /api/entities/service-accounts/v1alpha/batch-get
Read service accounts by id, built-in ids included. Ids that do not exist are left out.
# ListServiceAccounts
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaserviceaccountsservice/listserviceaccounts
/api-reference/openapi.yaml post /api/entities/service-accounts/v1alpha/list
List every workspace service account, then every built-in one, in the order they apply, and the
person links in a second list.
# ListServiceAccountSuggestions
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaserviceaccountsservice/listserviceaccountsuggestions
/api-reference/openapi.yaml post /api/entities/service-accounts/v1alpha/suggestions
Suggest service accounts for the logins the evidence says are not a person, each as a change set
with its evidence. A login a workspace account, a person link or Coalesce Quality takes is left
out. A login only a built-in account holds is suggested too, so it can get an account of its own. A suggestion changes nothing by itself: edit it if needed, plan it with
PlanServiceAccountChanges, then apply it.
# PlanServiceAccountChanges
Source: https://docs.synq.io/api-reference/synqentitiesplatformusersv1alphaserviceaccountsservice/planserviceaccountchanges
/api-reference/openapi.yaml post /api/entities/service-accounts/v1alpha/plan
Report what a change set would change, without writing it: every login slice whose
classification moves, how many counted reads each adds or removes, and the entities whose rank
moves most. The plan_token it returns is what ApplyServiceAccountChanges takes.
# BatchResolveIdentifiers
Source: https://docs.synq.io/api-reference/synqentitiesresolvev1identifierresolveservice/batchresolveidentifiers
/api-reference/openapi.yaml post /api/resolve/v1/by-id
Resolve multiple identifiers, returning their Coalesce Quality paths and identity groups.
# BatchSchemaMismatches
Source: https://docs.synq.io/api-reference/synqentitiesschemasv1schemamismatchesservice/batchschemamismatches
/api-reference/openapi.yaml post /api/schema-mismatches/v1
Get schema mismatches for a batch of entities by their identifiers.
# GetSchema
Source: https://docs.synq.io/api-reference/synqentitiesschemasv1schemasservice/getschema
/api-reference/openapi.yaml get /api/schema/v1
Get schema of the entity its identifier.
# BatchSqlDetails
Source: https://docs.synq.io/api-reference/synqentitiessql_insightsv1sqlinsightsservice/batchsqldetails
/api-reference/openapi.yaml post /api/sql-insights/v1/details:batch
BatchSqlDetails returns the SQL details for each of the requested entities,
keyed by their synq path: the set of detected SQL constructs and, when the
request asks for them, the joins that building the entity makes. Every
requested path appears in the response, including a path with no SQL
details and a path matching no entity; both are returned with an empty set
of constructs and no joins.
# ListJoinPatterns
Source: https://docs.synq.io/api-reference/synqentitiessql_insightsv1sqlinsightsservice/listjoinpatterns
/api-reference/openapi.yaml get /api/sql-insights/v1/join-patterns
ListJoinPatterns lists how other SQL joins a table, or one of its columns:
every join key that the SQL of a table, view or model in the workspace
uses to join the requested entity, read from the requested entity's side,
and which SQL makes it. It is the reverse of `BatchSqlDetails` joins, which
say how building one entity joins others: here the requested entity is the
one being joined.
The requested entity is taken together with every entity that is the same
object, like a dbt model, the table it builds and that table in a
catalog, so asking about any of them returns the joins on all of them.
# ListSqlConstructs
Source: https://docs.synq.io/api-reference/synqentitiessql_insightsv1sqlinsightsservice/listsqlconstructs
/api-reference/openapi.yaml get /api/sql-insights/v1/constructs
ListSqlConstructs returns the distribution of SQL constructs across all
entities in the workspace. Every count is a number of entities, not a number
of occurrences: an entity whose SQL contains five GROUP BY clauses adds one
to the HAS_GROUP_BY count, not five. The result is workspace-scoped (derived
from the authenticated request) — no entity paths are supplied. Useful for
building filter/autocomplete UIs and for high-level "shape of our SQL"
analytics.
# BatchGetIncidents
Source: https://docs.synq.io/api-reference/synqentitiesstatusv1entityincidentsservice/batchgetincidents
/api-reference/openapi.yaml post /api/status/v1/incidents/by-id
Get multiple entity statuses by their identifiers.
# BatchGetIssuesStatus
Source: https://docs.synq.io/api-reference/synqentitiesstatusv1entityissuesservice/batchgetissuesstatus
/api-reference/openapi.yaml post /api/status/v1/issues/by-id
Get multiple entity statuses by their identifiers.
# ListEntityTraitDefs
Source: https://docs.synq.io/api-reference/synqentitiestypedefsv1typedefsservice/listentitytraitdefs
/api-reference/openapi.yaml post /api/entities/typedefs/v1/entity-traits/list
List every trait a selection query can filter entities by.
Read it before writing a selection that uses `traits`, and to show a person
what one covers. A trait name this list does not contain selects nothing, so
reading the list is how you tell a trait that does not exist from one your
client is too old to know about.
# ListEntityTypeDefs
Source: https://docs.synq.io/api-reference/synqentitiestypedefsv1typedefsservice/listentitytypedefs
/api-reference/openapi.yaml post /api/entities/typedefs/v1/entity-types/list
List every kind of entity in your workspace, the built-in types and your own.
The built-in half covers every value of `synq.entities.v1.EntityType` this
server serves, so this is also how you discover a type that your generated code
is too old to name. Your own types have no enum value of their own — each one
is identified by its `type_id`.
# ListExecutionTypeDefs
Source: https://docs.synq.io/api-reference/synqentitiestypedefsv1typedefsservice/listexecutiontypedefs
/api-reference/openapi.yaml post /api/entities/typedefs/v1/execution-types/list
List every kind of run Coalesce Quality records against an entity.
A dbt invocation, a warehouse query job, a detected schema change. Use it to
name the `execution_type` that execution and run endpoints return.
# ListPlatformDefs
Source: https://docs.synq.io/api-reference/synqentitiestypedefsv1typedefsservice/listplatformdefs
/api-reference/openapi.yaml post /api/entities/typedefs/v1/platforms/list
List every platform Coalesce Quality connects to, with its display name.
Use it to name the platform on an entity — "BigQuery", "Microsoft SQL Server" —
rather than deriving a name from the enum value.
# Activate
Source: https://docs.synq.io/api-reference/synqextensionsatlanintegrationsv1atlanintegrationservice/activate
/api-reference/openapi.yaml patch /api/extensions/atlan/integrations/v1
Activate or deactivate the integration.
# Get
Source: https://docs.synq.io/api-reference/synqextensionsatlanintegrationsv1atlanintegrationservice/get
/api-reference/openapi.yaml get /api/extensions/atlan/integrations/v1
# Ping
Source: https://docs.synq.io/api-reference/synqextensionsatlanintegrationsv1atlanintegrationservice/ping
/api-reference/openapi.yaml post /api/extensions/atlan/integrations/v1/ping
Pings Atlan with the given connection credentials.
# Remove
Source: https://docs.synq.io/api-reference/synqextensionsatlanintegrationsv1atlanintegrationservice/remove
/api-reference/openapi.yaml delete /api/extensions/atlan/integrations/v1
# Upsert
Source: https://docs.synq.io/api-reference/synqextensionsatlanintegrationsv1atlanintegrationservice/upsert
/api-reference/openapi.yaml post /api/extensions/atlan/integrations/v1
Upserts an integration and validates it by checking if the provided API URL and token are valid.
# GetAtlanConnections
Source: https://docs.synq.io/api-reference/synqextensionsatlanproviderv1atlanproviderservice/getatlanconnections
/api-reference/openapi.yaml get /api/extensions/atlan/provider/v1/connections
# GetAtlanDataProducts
Source: https://docs.synq.io/api-reference/synqextensionsatlanproviderv1atlanproviderservice/getatlandataproducts
/api-reference/openapi.yaml get /api/extensions/atlan/provider/v1/data-products
# GetAtlanDomains
Source: https://docs.synq.io/api-reference/synqextensionsatlanproviderv1atlanproviderservice/getatlandomains
/api-reference/openapi.yaml get /api/extensions/atlan/provider/v1/domains
# FetchRuns
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/fetchruns
/api-reference/openapi.yaml get /api/extensions/atlan/workflows/v1/runs
# GetConnectionMappings
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/getconnectionmappings
/api-reference/openapi.yaml get /api/extensions/atlan/workflows/v1/connection-mappings
# GetDomainMappings
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/getdomainmappings
/api-reference/openapi.yaml get /api/extensions/atlan/workflows/v1/domain-mappings
# GetMappingToggles
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/getmappingtoggles
/api-reference/openapi.yaml get /api/extensions/atlan/workflows/v1/mappings/toggle
# GetProductMappings
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/getproductmappings
/api-reference/openapi.yaml get /api/extensions/atlan/workflows/v1/product-mappings
# RemoveConnectionMappings
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/removeconnectionmappings
/api-reference/openapi.yaml delete /api/extensions/atlan/workflows/v1/connection-mappings
# SetConnectionMappings
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/setconnectionmappings
/api-reference/openapi.yaml post /api/extensions/atlan/workflows/v1/connection-mappings
# SetMappingToggles
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/setmappingtoggles
/api-reference/openapi.yaml post /api/extensions/atlan/workflows/v1/mappings/toggle
# Synchronize
Source: https://docs.synq.io/api-reference/synqextensionsatlanworkflowsv1atlanworkflowservice/synchronize
/api-reference/openapi.yaml post /api/extensions/atlan/workflows/v1/synchronize
Run a synchronization job for the integration.
# GetDiffByChangeId
Source: https://docs.synq.io/api-reference/synqgitcommitsv1commitsservice/getdiffbychangeid
/api-reference/openapi.yaml get /api/git/v1/change/{change_id}
# GetDiffByCommit
Source: https://docs.synq.io/api-reference/synqgitcommitsv1commitsservice/getdiffbycommit
/api-reference/openapi.yaml get /api/git/v1/commit/{commit_sha}
# AddIssuesToIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/addissuestoincident
/api-reference/openapi.yaml post /api/incidents/v1/{incident_id}/issues
# AssignIncidentOwner
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/assignincidentowner
/api-reference/openapi.yaml patch /api/incidents/v1/{incident_id}/assign-owner
# BatchGetIncidents
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/batchgetincidents
/api-reference/openapi.yaml post /api/incidents/v1/by-id
# CancelIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/cancelincident
/api-reference/openapi.yaml patch /api/incidents/v1/{incident_id}/cancel
Cancel an incident
# CloseIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/closeincident
/api-reference/openapi.yaml patch /api/incidents/v1/{incident_id}/close
Close an incident
# CreateIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/createincident
/api-reference/openapi.yaml post /api/incidents/v1
# DeleteComment
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/deletecomment
/api-reference/openapi.yaml delete /api/incidents/v1/{incident_id}/comment/{comment_id}
Delete a comment on an incident. Only the actor who posted the comment may
delete it. Deleting an already-deleted comment succeeds.
# ListIncidents
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/listincidents
/api-reference/openapi.yaml get /api/incidents/v1
# MergeIncidents
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/mergeincidents
/api-reference/openapi.yaml post /api/incidents/v1/{incident_id}/merge
# PostComment
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/postcomment
/api-reference/openapi.yaml post /api/incidents/v1/{incident_id}/comment
Post a comment on an incident.
# RemoveIssuesFromIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/removeissuesfromincident
/api-reference/openapi.yaml delete /api/incidents/v1/{incident_id}/issues
# RenameIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/renameincident
/api-reference/openapi.yaml patch /api/incidents/v1/{incident_id}/rename
# SplitIncident
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/splitincident
/api-reference/openapi.yaml post /api/incidents/v1/{incident_id}/split
# UpdateIncidentState
Source: https://docs.synq.io/api-reference/synqincidentsv1incidentsservice/updateincidentstate
/api-reference/openapi.yaml patch /api/incidents/v1/{incident_id}/state
# IngestDatahubAspects
Source: https://docs.synq.io/api-reference/synqingestairflowv1airflowlogsservice/ingestdatahubaspects
/api-reference/openapi.yaml post /api/ingest/airflow/v1/aspects
Accepts what a DataHub REST emitter sends when its server is set to
`/api/ingest/airflow/v1`: it posts to `{server}/aspects?action=ingestProposal`.
# IngestLog
Source: https://docs.synq.io/api-reference/synqingestairflowv1airflowlogsservice/ingestlog
/api-reference/openapi.yaml post /api/ingest/airflow/v1/{dag_id}/{task_id}/{run_id}/logs
# IngestCloudwatchLogsData
Source: https://docs.synq.io/api-reference/synqingestcloudwatchv1cloudwatchservice/ingestcloudwatchlogsdata
/api-reference/openapi.yaml post /api/ingest/cloudwatch/v1/{log_group}/{log_stream}
# IngestLineage
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingestlineage
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/lineage
IngestLineage pushes data-flow lineage between warehouse objects (and,
optionally, between their columns) into the platform. Send the lineage you
extract from the warehouse — for example from Snowflake's GET_LINEAGE — as a
batch of directed edges. Use `replace_mode` to choose between an
incremental update (MERGE) and an authoritative refresh of a region of the
warehouse (REPLACE_SCOPE); REPLACE_SCOPE is what lets the platform expire
edges that no longer exist. Safe to retry: re-sending the same batch
converges to the same state.
# IngestObjectInformation
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingestobjectinformation
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/object-information
# IngestObjectMetrics
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingestobjectmetrics
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/object-metrics
# IngestQueryLogs
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingestquerylogs
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/query-logs
# IngestSchemas
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingestschemas
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/schemas
# IngestSqlDefinitions
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingestsqldefinitions
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/sql-definitions
# IngestTableConstraints
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingesttableconstraints
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/upload/{upload_id}/table-constraints
# IngestTaskResults
Source: https://docs.synq.io/api-reference/synqingestdwhv1dwhservice/ingesttaskresults
/api-reference/openapi.yaml post /api/datawarehouse/v1/connection/{connection_id}/task/{task_id}/task-results
# Ingest
Source: https://docs.synq.io/api-reference/synqingestopenlineagev1openlineageservice/ingest
/api-reference/openapi.yaml post /api/ingest/openlineage/v1
# BatchGetIntegrations
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/batchgetintegrations
/api-reference/openapi.yaml post /api/integrations/v1/by-id
Fetch multiple integrations by id in one call. Ids not found (or not in the
workspace) are omitted from the response rather than erroring.
# CreateIntegration
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/createintegration
/api-reference/openapi.yaml post /api/integrations/v1
Create a new integration. The server assigns the id and returns the created
integration including any generated outputs.
The integration type is fixed by the populated `config` variant and cannot
be changed later. Counts against the per-workspace quota.
Not safe to blindly retry: the server mints a new id on each call, so a
retried create produces a second integration. On a transport error, list /
look up before retrying.
# DeleteIntegration
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/deleteintegration
/api-reference/openapi.yaml delete /api/integrations/v1/{integration_id}
Delete an integration. The integration stops being scheduled and is removed
from listings. Safe to retry (deleting an already-deleted id is a no-op).
# DisableIntegration
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/disableintegration
/api-reference/openapi.yaml post /api/integrations/v1/{integration_id}/disable
Disable an integration without deleting it. The configuration is kept but
the integration is no longer scheduled or refreshed. No-op if already
disabled.
# EnableIntegration
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/enableintegration
/api-reference/openapi.yaml post /api/integrations/v1/{integration_id}/enable
Enable a previously disabled integration. No-op if already enabled.
# ListIntegrations
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/listintegrations
/api-reference/openapi.yaml get /api/integrations/v1
List all integrations in the workspace (optionally filtered by enabled
state). Secret config fields are masked. The full set is returned in one
response — a workspace has only a handful of integrations.
# RefreshIntegration
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/refreshintegration
/api-reference/openapi.yaml post /api/integrations/v1/{integration_id}/refresh
Trigger an immediate, ad-hoc refresh of an integration. Available only for
types whose `Capabilities.can_refresh` is true; otherwise rejected with
FAILED_PRECONDITION.
Not idempotent: each call enqueues a new refresh.
# UpdateIntegration
Source: https://docs.synq.io/api-reference/synqintegrationsv1integrationsservice/updateintegration
/api-reference/openapi.yaml put /api/integrations/v1/{integration_id}
Update an integration. The `config` is replaced wholesale — send the
complete desired config (omitted secrets are preserved; see the
service-level secret semantics). `title` is patch-style: omit it to leave
it unchanged.
The `config` variant must match the existing integration's type; changing
the type is rejected with FAILED_PRECONDITION.
Safe to retry: addresses an existing id and converges to the same state.
With an `etag` set, a retry after success returns ABORTED
(the version moved) — treat that as "already applied".
# BatchGetIssues
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/batchgetissues
/api-reference/openapi.yaml post /api/issues/v1/by-id
# ListIssues
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/listissues
/api-reference/openapi.yaml get /api/issues/v1
# MarkExpected
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/markexpected
/api-reference/openapi.yaml patch /api/issues/v1/{issue_id}/mark-expected
Mark issue as expected.
# MarkFixed
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/markfixed
/api-reference/openapi.yaml patch /api/issues/v1/{issue_id}/mark-fixed
Mark issue as fixed.
# MarkInvestigating
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/markinvestigating
/api-reference/openapi.yaml patch /api/issues/v1/{issue_id}/mark-investigating
Mark issue as being investigated.
# MarkNoActionNeeded
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/marknoactionneeded
/api-reference/openapi.yaml patch /api/issues/v1/{issue_id}/mark-no-action-needed
Mark issue as no action needed.
# PostComment
Source: https://docs.synq.io/api-reference/synqissuesissuesv1issuesservice/postcomment
/api-reference/openapi.yaml post /api/issues/v1/{issue_id}/comment
Post a comment on an issue.
# BatchGetIssues
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/batchgetissues
/api-reference/openapi.yaml post /api/issues/v2/by-id
# ClearStatus
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/clearstatus
/api-reference/openapi.yaml patch /api/issues/v2/{issue_id}/clear-status
Clear the triage status of an issue, returning it to an untriaged state.
Reverses a previous MarkInvestigating / MarkFixed / MarkExpected /
MarkNoActionNeeded so the issue shows up again in open/untriaged views.
# DeleteComment
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/deletecomment
/api-reference/openapi.yaml delete /api/issues/v2/{issue_id}/comment/{comment_id}
Delete a comment on an issue. Only the actor who posted the comment may
delete it. Deleting an already-deleted comment succeeds.
# ListIssues
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/listissues
/api-reference/openapi.yaml get /api/issues/v2
# MarkExpected
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/markexpected
/api-reference/openapi.yaml patch /api/issues/v2/{issue_id}/mark-expected
Mark issue as expected.
# MarkFixed
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/markfixed
/api-reference/openapi.yaml patch /api/issues/v2/{issue_id}/mark-fixed
Mark issue as fixed.
# MarkInvestigating
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/markinvestigating
/api-reference/openapi.yaml patch /api/issues/v2/{issue_id}/mark-investigating
Mark issue as being investigated.
# MarkNoActionNeeded
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/marknoactionneeded
/api-reference/openapi.yaml patch /api/issues/v2/{issue_id}/mark-no-action-needed
Mark issue as no action needed.
# PostComment
Source: https://docs.synq.io/api-reference/synqissuesv2issuesservice/postcomment
/api-reference/openapi.yaml post /api/issues/v2/{issue_id}/comment
Post a comment on an issue.
# BatchGetEntityKnowledge
Source: https://docs.synq.io/api-reference/synqknowledgev1alphaknowledgeservice/batchgetentityknowledge
/api-reference/openapi.yaml post /api/knowledge/v1alpha/entities/by-id
Read the knowledge held about entities: the description people wrote for
each one, and the knowledge pages pinned to it.
An entity with no knowledge is absent from the response, as is an id that
names no entity. Returns UNAVAILABLE when the knowledge could not be read,
which is worth retrying.
# BatchGetKnowledgePages
Source: https://docs.synq.io/api-reference/synqknowledgev1alphaknowledgeservice/batchgetknowledgepages
/api-reference/openapi.yaml post /api/knowledge/v1alpha/pages/by-id
Read knowledge pages by id: their content, where they sit in the tree,
who owns them and how they are tagged. A page that does not exist, or was
deleted, is absent from the response. Set `skip_content` to read
everything but the content, for showing a listing.
# ListKnowledgePageLinks
Source: https://docs.synq.io/api-reference/synqknowledgev1alphaknowledgeservice/listknowledgepagelinks
/api-reference/openapi.yaml get /api/knowledge/v1alpha/pages/{page_id}/links
List what is pinned to a knowledge page, in either direction: the ids of
the entities and of the other knowledge pages.
# ListKnowledgePages
Source: https://docs.synq.io/api-reference/synqknowledgev1alphaknowledgeservice/listknowledgepages
/api-reference/openapi.yaml get /api/knowledge/v1alpha/pages
List the ids of knowledge pages, ordered by name, optionally narrowed by a
part of their name and by certification. Deprecated pages are left out
unless asked for. Read the pages with `BatchGetKnowledgePages`.
# BatchDeleteDeploymentRules
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/batchdeletedeploymentrules
/api-reference/openapi.yaml delete /api/monitors/automated/v1/deployment-rules/batch
# BatchDeployDeploymentRules
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/batchdeploydeploymentrules
/api-reference/openapi.yaml post /api/monitors/automated/v1/deployment-rules/batch/deployed
# BatchGetDeploymentRules
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/batchgetdeploymentrules
/api-reference/openapi.yaml get /api/monitors/automated/v1/deployment-rules/batch
# BatchUpdateDeploymentRuleTitle
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/batchupdatedeploymentruletitle
/api-reference/openapi.yaml patch /api/monitors/automated/v1/deployment-rules
# BatchUpsertDeploymentRules
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/batchupsertdeploymentrules
/api-reference/openapi.yaml post /api/monitors/automated/v1/deployment-rules/batch
# DeleteDeploymentRule
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/deletedeploymentrule
/api-reference/openapi.yaml delete /api/monitors/automated/v1/deployment-rule/{id}
# DeployDeploymentRule
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/deploydeploymentrule
/api-reference/openapi.yaml post /api/monitors/automated/v1/deployment-rules/deployed
# GetDeployDeleteOverview
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/getdeploydeleteoverview
/api-reference/openapi.yaml get /api/monitors/automated/v1/deployment-rule/{id}/deleted
# GetDeployOverview
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/getdeployoverview
/api-reference/openapi.yaml get /api/monitors/automated/v1/deployment-rule
# ListDeploymentRules
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/listdeploymentrules
/api-reference/openapi.yaml get /api/monitors/automated/v1/deployment-rules
# ListDeploymentRulesForAsset
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/listdeploymentrulesforasset
/api-reference/openapi.yaml get /api/monitors/automated/v1/deployment-rules/{asset_path}
# UpsertDeploymentRule
Source: https://docs.synq.io/api-reference/synqmonitorsautomated_monitorsv1deploymentrulesservice/upsertdeploymentrule
/api-reference/openapi.yaml post /api/monitors/automated/v1/deployment-rules
# BatchCreateMonitor
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/batchcreatemonitor
/api-reference/openapi.yaml post /api/monitors/custom-monitors/v1
# BatchDeleteMonitor
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/batchdeletemonitor
/api-reference/openapi.yaml post /api/monitors/custom-monitors/v1/delete
# BatchUpdateMonitor
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/batchupdatemonitor
/api-reference/openapi.yaml put /api/monitors/custom-monitors/v1
# BatchUpdateMonitorMode
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/batchupdatemonitormode
/api-reference/openapi.yaml post /api/monitors/custom-monitors/v1/mode
# BatchUpdateMonitorSeverity
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/batchupdatemonitorseverity
/api-reference/openapi.yaml post /api/monitors/custom-monitors/v1/severity
# ConfigChangesOverview
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/configchangesoverview
/api-reference/openapi.yaml post /api/monitors/custom-monitors/v1/config-changes
# ListConfigsMonitors
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/listconfigsmonitors
/api-reference/openapi.yaml get /api/monitors/custom-monitors/v1/by-config
# ListMonitors
Source: https://docs.synq.io/api-reference/synqmonitorscustom_monitorsv1custommonitorsservice/listmonitors
/api-reference/openapi.yaml get /api/monitors/custom-monitors/v1
# History
Source: https://docs.synq.io/api-reference/synqmonitorshistoryv1historyservice/history
/api-reference/openapi.yaml get /api/monitors/history/v1
# MonitorInfo
Source: https://docs.synq.io/api-reference/synqmonitorsinfov1monitorinfoservice/monitorinfo
/api-reference/openapi.yaml get /api/monitors/info/v1
# BatchGetLatestEntityMetrics
Source: https://docs.synq.io/api-reference/synqmonitorspredictionsv1monitorpredictionsservice/batchgetlatestentitymetrics
/api-reference/openapi.yaml post /api/monitors/predictions/v1/entity-metrics:batchLatest
Returns the most recent observed table-stats sample (row count, freshness) for each
requested entity. Identity-aware: resolves logical assets to their underlying physical
tables. Paths with no recent sample are omitted from the response rather than returned
with empty values — callers should treat a missing entry as "no data". Lookback matches
GetEntityMetrics (30 days).
# GetEntityMetrics
Source: https://docs.synq.io/api-reference/synqmonitorspredictionsv1monitorpredictionsservice/getentitymetrics
/api-reference/openapi.yaml get /api/monitors/predictions/v1/entity-metrics
Returns raw metric timeseries for an entity. Identity-aware: resolves
logical assets (e.g., dbt models) to their underlying physical tables.
# GetMonitorPredictions
Source: https://docs.synq.io/api-reference/synqmonitorspredictionsv1monitorpredictionsservice/getmonitorpredictions
/api-reference/openapi.yaml get /api/monitors/predictions/v1
Returns prediction timeseries for a monitor. The monitor must exist
and not be deleted. Defaults to the monitor's current metrics_version.
# BatchUpdateMonitorSegmentLabels
Source: https://docs.synq.io/api-reference/synqmonitorssegmentsv1monitorsegmentsservice/batchupdatemonitorsegmentlabels
/api-reference/openapi.yaml post /api/monitors/segments/v1/labels
Sets or clears the label of one or more segments.
Segments are addressed by the id ListMonitorSegments returns. A segment id
encodes a sanitised form of the segment value, so it cannot be constructed
from that value: read the ids first, then write against them.
# ListMonitorSegments
Source: https://docs.synq.io/api-reference/synqmonitorssegmentsv1monitorsegmentsservice/listmonitorsegments
/api-reference/openapi.yaml get /api/monitors/segments/v1
Lists the segments a monitor has ingested so far, with their raw value and
current label. A segment appears only once data has been observed for it,
so a monitor that has not run yet lists none.
# BatchGetEntityOverlays
Source: https://docs.synq.io/api-reference/synqoverlaysv1overlaysservice/batchgetentityoverlays
/api-reference/openapi.yaml post /api/overlays/v1/by-id
For each entity, list every overlay that claims it.
The response has one entry per requested id, in the order you asked, so you
can match results positionally and never have to tell "absent" from "empty".
An entity that belongs to no overlay comes back with empty lists.
Freshness. Which overlays exist and what they are attached to is up to date
the moment you change it — create an overlay, delete one, point an ownership
at a different data product, and the next call reflects it. What each
overlay *contains* is recomputed periodically, so an asset that newly
satisfies an existing rule — a table appearing in a folder a rule watches —
shows up shortly afterwards rather than instantly.
Returns PERMISSION_DENIED if your credential can read neither data products
nor owners. If it can read one but not the other, the call succeeds and the
list you may not read comes back empty rather than failing.
# BatchGetOverlayMemberCounts
Source: https://docs.synq.io/api-reference/synqoverlaysv1overlaysservice/batchgetoverlaymembercounts
/api-reference/openapi.yaml post /api/overlays/v1/member-counts
Count the members of each named overlay.
The cheap way to answer "how big is this" — use it for a summary line or a
badge. When you need the assets themselves, list them through the service
that defines the overlay.
Counts distinct members, so an owner responsible for two data products that
overlap is not charged twice for the assets they share.
Same freshness as `BatchGetEntityOverlays`: counts follow the periodic
recomputation of what each overlay contains.
Returns PERMISSION_DENIED if your credential can read neither data products
nor owners.
# GetCoverageSummary
Source: https://docs.synq.io/api-reference/synqoverlaysv1overlaysservice/getcoveragesummary
/api-reference/openapi.yaml post /api/overlays/v1/coverage-summary
Count coverage over one or more named candidate sets, without listing the
assets themselves.
The aggregate behind a coverage dashboard. Each selection you send comes
back as one row of totals, keyed by the label you gave it, so a breakdown
by type, platform, folder or domain is one round trip rather than one call
per bucket.
There is no server-side grouping, on purpose. This service knows what claims
an asset, not what kind of thing the asset is, so naming the selections
yourself — `with_type("dbt_model")`, `in_folder(...)`, `in_domain(...)` —
keeps the buckets in your own vocabulary and costs the same.
Selections are counted independently and may overlap. An asset in two of
them is counted in both.
Returns PERMISSION_DENIED unless your credential can read both data products
and owners. Every row names both, and a zero that was never looked at is
indistinguishable from a zero that was.
# ListEntityCoverage
Source: https://docs.synq.io/api-reference/synqoverlaysv1overlaysservice/listentitycoverage
/api-reference/openapi.yaml post /api/overlays/v1/coverage
List the candidate assets together with the overlays claiming each, and
narrow them to what is or is not covered.
This is the question behind a governance audit: of the assets you care
about, which belong to no data product, and which have nobody responsible
for them. You give the candidate set as a query — every asset, one
platform, one folder, one domain — and each row comes back carrying the
same owner and data-product lists `BatchGetEntityOverlays` returns, so a
single call is both the count and the worklist.
`total_count` is how many candidates match `filter` across every page, so
you can say "312 of 4,190 tables are in no data product" without paging to
the end.
Freshness. By default this reads the same periodically recomputed
membership the rest of this service reads, and tells you how old it is in
`members_computed_at`. Ask for `FRESHNESS_LIVE` when the answer has to
reflect every definition as of now — it re-resolves them, which is far
slower and far more expensive, so keep it for a one-off audit rather than a
page that refreshes.
Returns PERMISSION_DENIED if your credential can read neither data products
nor owners, and also if `filter` names a kind it may not read: an empty list
must not be the answer to both "nothing is uncovered" and "you may not
look". A kind you may not read still comes back as an empty list on each
row, as it does elsewhere in this service.
Returns INVALID_ARGUMENT if the selection resolves to more assets than one
call will report on — see `CoverageSelection` for the limit and for how to
choose a candidate set worth reading.
# BatchGetOwners
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/batchgetowners
/api-reference/openapi.yaml post /api/owners/v1/batch-get
Fetch multiple owners by id. Ids that do not exist (or, unless
`include_deleted` is set, are deleted) are omitted from the response rather
than causing an error.
# BatchGetOwnerships
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/batchgetownerships
/api-reference/openapi.yaml post /api/owners/v1/ownerships/batch-get
Fetch multiple ownerships by id (across owners). Ids that do not exist (or,
unless `include_deleted` is set, are deleted) are omitted.
# DeleteOwner
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/deleteowner
/api-reference/openapi.yaml post /api/owners/v1/delete
Delete an owner and all of its ownerships. Idempotent — deleting an
already-deleted or unknown owner succeeds. Soft-deletes by default; set
`purge` to hard-delete and release the id. Pass `etag` to guard against
deleting a concurrently-edited owner.
# DeleteOwnership
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/deleteownership
/api-reference/openapi.yaml post /api/owners/v1/ownerships/delete
Delete an ownership by id. Idempotent — deleting an already-deleted or
unknown ownership succeeds. Pass `etag` to guard against deleting a
concurrently-edited ownership.
# ListOwners
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/listowners
/api-reference/openapi.yaml get /api/owners/v1
List the owners in the workspace, paginated.
# ListOwnerships
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/listownerships
/api-reference/openapi.yaml get /api/owners/v1/{owner_id}/ownerships
List the ownerships of an owner, paginated.
# UpsertOwner
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/upsertowner
/api-reference/openapi.yaml post /api/owners/v1
Create or update an owner. The id is supplied by the caller (a UUID);
repeating the same request converges to the same owner. Mutable fields are
optional: a set field is written, an omitted field is left unchanged. Pass
`etag` to guard against overwriting a concurrent edit.
# UpsertOwnership
Source: https://docs.synq.io/api-reference/synqownersv1ownersservice/upsertownership
/api-reference/openapi.yaml post /api/owners/v1/{owner_id}/ownerships
Create or update an ownership under an owner. The ownership id is supplied
by the caller (a UUID). Assigning a data product that is already owned by a
different ownership moves it (the previous ownership of that product is
removed), matching the app.
# SetPlatformAuthentication
Source: https://docs.synq.io/api-reference/synqplatformsv1platformsservice/setplatformauthentication
/api-reference/openapi.yaml post /api/platforms/v1
# GenerateQuery
Source: https://docs.synq.io/api-reference/synqqueriesv1nlqueryservice/generatequery
/api-reference/openapi.yaml post /api/queries/v1/generate
GenerateQuery generates a Query from a natural language description.
# DescribeResolverQl
Source: https://docs.synq.io/api-reference/synqqueriesv1queriesservice/describeresolverql
/api-reference/openapi.yaml post /api/queries/v1/describe-resolver-ql
Describe the ResolverQL this deployment accepts: every function with its
arguments and documentation, the category order to group them by, and the
accepted values of every enum-typed argument.
Read it before authoring a selection, and validate against it rather than
against a copy compiled into your client. The language gains functions and
accepted values as Coalesce Quality supports more, and a client working from
its own table both misses what was added and refuses a query this server
would compile.
Nothing is passed, and no permission is needed beyond being authenticated —
so a client holding a narrow credential, one that may write a deployment
rule but cannot read entities, can still find out how to write the selection
it is being asked for.
Read it as an answer for your workspace, not as a constant. Most of it is
the same for everyone, but the entity types you defined yourself are part of
the language too: today they are reachable only through
`ResolverQlEnumDomain.pattern`, which is why that field exists, and named by
`synq.entities.typedefs.v1.TypeDefsService.ListEntityTypeDefs`.
Caching it for the life of your process is fine; re-read it after you add or
rename an entity type of your own.
# ResolveQuery
Source: https://docs.synq.io/api-reference/synqqueriesv1queriesservice/resolvequery
/api-reference/openapi.yaml post /api/queries/v1/resolve
Evaluate a selection and return the matched entity ids, paginated. Pass a
ResolverQL expression via `resolver_ql` (it is compiled server-side) or an
already-structured `query`. Invalid ResolverQL fails with INVALID_ARGUMENT.
# TranslateResolverQl
Source: https://docs.synq.io/api-reference/synqqueriesv1queriesservice/translateresolverql
/api-reference/openapi.yaml post /api/queries/v1/translate-resolver-ql
Compile a ResolverQL expression into a structured public `Query`. Unlike
`ResolveQuery`, it does not evaluate the query against the catalog. A
syntactically invalid or uncompilable expression fails with
INVALID_ARGUMENT.
# BatchGetRecommendations
Source: https://docs.synq.io/api-reference/synqrecommendationsv1recommendationsservice/batchgetrecommendations
/api-reference/openapi.yaml post /api/recommendations/v1/by-id
Fetch recommendations by id. Ids that are unknown, belong to another
workspace, no longer exist because the condition cleared, or concern an
integration the credential cannot read are omitted from the response
rather than erroring.
# DismissRecommendation
Source: https://docs.synq.io/api-reference/synqrecommendationsv1recommendationsservice/dismissrecommendation
/api-reference/openapi.yaml post /api/recommendations/v1/{recommendation_id}/dismiss
Dismiss a recommendation until something new is found about it. Replaces
an ignore. Dismissing again records the recommendation as it reads now,
so it stays hidden until something newer than that is found.
Returns NOT_FOUND when the recommendation does not exist in the
workspace, and PERMISSION_DENIED when it concerns an integration the
credential cannot edit.
# IgnoreRecommendation
Source: https://docs.synq.io/api-reference/synqrecommendationsv1recommendationsservice/ignorerecommendation
/api-reference/openapi.yaml post /api/recommendations/v1/{recommendation_id}/ignore
Ignore a recommendation for good, whatever is found about it later.
Replaces a dismissal. Ignoring an ignored recommendation succeeds without
change.
Returns NOT_FOUND when the recommendation does not exist in the
workspace, and PERMISSION_DENIED when it concerns an integration the
credential cannot edit.
# ListRecommendations
Source: https://docs.synq.io/api-reference/synqrecommendationsv1recommendationsservice/listrecommendations
/api-reference/openapi.yaml get /api/recommendations/v1
List the ids of the workspace's recommendations, optionally limited by
target and state. Fetch the recommendations themselves with
`BatchGetRecommendations`. Ids are returned in a stable order, so paging
is consistent while recommendations are added or removed.
Returns INVALID_ARGUMENT for a malformed cursor.
# RestoreRecommendation
Source: https://docs.synq.io/api-reference/synqrecommendationsv1recommendationsservice/restorerecommendation
/api-reference/openapi.yaml post /api/recommendations/v1/{recommendation_id}/restore
Remove the dismissal or ignore standing against a recommendation, so it
is open again. Restoring a recommendation nobody decided about succeeds
without change.
Returns NOT_FOUND when the recommendation does not exist in the
workspace, and PERMISSION_DENIED when it concerns an integration the
credential cannot edit.
# BatchGet
Source: https://docs.synq.io/api-reference/synqsavedviewsv1savedviewsservice/batchget
/api-reference/openapi.yaml post /api/saved-views/v1/batch-get
Fetch multiple saved views by id. Ids the caller cannot see, or that do not
exist, are omitted from the response rather than causing an error.
# Delete
Source: https://docs.synq.io/api-reference/synqsavedviewsv1savedviewsservice/delete
/api-reference/openapi.yaml post /api/saved-views/v1/delete
Delete a saved view. Idempotent — deleting an already-deleted or unknown
view succeeds. Pass `etag` to guard against deleting a concurrently-edited
view.
# List
Source: https://docs.synq.io/api-reference/synqsavedviewsv1savedviewsservice/list
/api-reference/openapi.yaml post /api/saved-views/v1/list
List the saved views the calling user can see. By default returns every
visible view (their own, workspace-owned, shared-with-workspace and
granted-to-them); narrow with `scopes`, `context` and `only_pinned`.
# SetPin
Source: https://docs.synq.io/api-reference/synqsavedviewsv1savedviewsservice/setpin
/api-reference/openapi.yaml post /api/saved-views/v1/set-pin
Pin or unpin a saved view to the calling user's main screen. Pinning is a
per-user preference: it applies only to the caller and does not modify the
view or its version, so any user who can see a view may pin it.
# Upsert
Source: https://docs.synq.io/api-reference/synqsavedviewsv1savedviewsservice/upsert
/api-reference/openapi.yaml post /api/saved-views/v1
Create or update a saved view.
The view id is supplied by the caller (a UUID), which makes this operation
idempotent — repeating the same request converges to the same view rather
than creating a duplicate. Mutable fields are optional: a field that is set
is written, a field that is omitted is left unchanged. To guard against
overwriting a concurrent edit, pass the `etag` you last read.
# PreviewSchedules
Source: https://docs.synq.io/api-reference/synqschedulev1scheduleservice/previewschedules
/api-reference/openapi.yaml post /api/schedule/v1/preview
PreviewSchedules returns the next occurrences for one or more schedule expressions.
Never fails at the RPC level; per-schedule errors are returned in the response items.
Response items match request items by index.
# AddUserRole
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/adduserrole
/api-reference/openapi.yaml post /api/users/v1/roles
Grants a role to a user in the workspace.
Granting a role the user already holds succeeds without change. Granting a
role to a user who is not a member of the workspace makes them a member;
such a user can only be named by their synq or email identity.
# BatchGetUsers
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/batchgetusers
/api-reference/openapi.yaml post /api/users/v1/batch-get
# DeleteUserAliases
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/deleteuseraliases
/api-reference/openapi.yaml post /api/users/v1/aliases/delete
Removes aliases from users.
A platform user alias removes only that login, every other alias type removes
the user's alias of that type. The scopes are checked per alias, as for
UpsertUserAliases.
# GetByAlias
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/getbyalias
/api-reference/openapi.yaml post /api/users/v1/by-alias
# GetByEmail
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/getbyemail
/api-reference/openapi.yaml get /api/users/v1/by-email
# GetUserAliases
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/getuseraliases
/api-reference/openapi.yaml get /api/users/v1/aliases
# GetUserIdentities
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/getuseridentities
/api-reference/openapi.yaml get /api/users/v1/identities
Returns all canonical identity strings for a user.
If user_id is omitted, returns identities for the currently authenticated user.
These identity strings can be passed to ListIssues(interacted_by_identities)
to filter issues the user has interacted with.
Format: "synq:", "email:", "slack:", "msteams:",
and the "platform_user:" id of every linked platform user.
# ListUsers
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/listusers
/api-reference/openapi.yaml get /api/users/v1
# RemoveUserRole
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/removeuserrole
/api-reference/openapi.yaml post /api/users/v1/roles/remove
Withdraws a role from a user in the workspace.
Withdrawing a role the user does not hold succeeds without change.
Withdrawing the user's last role removes them from the workspace, together
with their linked aliases.
# UpsertUserAliases
Source: https://docs.synq.io/api-reference/synqusersv1usersservice/upsertuseraliases
/api-reference/openapi.yaml put /api/users/v1/aliases
Links aliases to users.
A platform user alias needs SCOPE_PLATFORM_USER_LINK, every other alias type
SCOPE_USER_EDIT. A request holding an alias the caller may not write is
refused as a whole, and nothing is written.
# Using Webhooks
Source: https://docs.synq.io/api-reference/webhook
Getting started with developer API at Coalesce Quality
# Using Webhooks to Keep External Applications Up-to-Date
Webhooks are a powerful way to receive real-time notifications from Coalesce Quality when specific events occur. This allows external applications to stay updated without constant polling, saving resources and ensuring timely updates.
## Introduction to Webhooks
Coalesce Quality sends HTTP POST requests to a predefined URL whenever certain events occur, including issue lifecycle changes, incident notifications, or a simple ping to confirm that the webhook endpoint is reachable.
Webhooks are defined by a schema that ensures the correct data structure is maintained. Each event has a specific payload format, allowing external applications to process the data accordingly.
Latest schema definition is available as a [JSON Schema](https://schemas.synq.io/webhook/v1/event.schema.json) and as [HTML documentation](https://schemas.synq.io/webhook/v1/event.html).
### Key Concepts
* **Event Types**: Webhook events can represent different actions, such as `ping`, `issue_created`, `issue_updated`, `issue_status_updated`, `issue_closed`, `incident_open`, `incident_closed`, and `incident_cancelled`.
* **Payload Structure**: Each event payload adheres to a defined JSON schema, ensuring consistency and reliability in the data received.
* **Callback Mechanisms**: Webhooks can trigger specific commands or actions in the receiving application, making it possible to automate workflows based on incoming events.
## Webhook Event Schema
The webhook event schema defines the structure of the payload sent for each event type:
* **`workspace`**: Identifies the workspace where the event occurred.
* **`event_id`**: A unique identifier for the event.
* **`event_time`**: The time when the event occurred, formatted as a date-time string.
* **`event_type`**: One of the specific event types (`ping`, `issue_created`, `issue_updated`, `issue_status_updated`, `issue_closed`, `incident_open`, `incident_closed`, `incident_cancelled`), each with its own structured payload.
* **`callbacks`**: An array of callbacks that can be invoked based on the event, containing details such as `url`, `action_name`, and associated `issues_command`.
### Event Types
#### Issue Events
* **Issue Created**: Notifies that a new issue has been created. Contains an `IssueSummary` with details such as `issue_id`, `title`, `description`, `status`, trigger and affected entities, and `owners` (the people or teams responsible for the affected entities).
* **Issue Updated**: Indicates that an existing issue has been updated.
* **Issue Status Updated**: Signals that the status of an issue has changed (e.g., investigating, expected, fixed, no action needed).
* **Issue Closed**: Signals that an issue has been closed.
#### Incident Events
* **Incident Open**: Notifies that a new incident has been opened. Contains an `IncidentSummary` with `incident_id`, `title`, `description`, and `incident_url`.
* **Incident Closed**: Indicates that an incident has been closed.
* **Incident Cancelled**: Signals that an incident has been cancelled.
#### Other Events
* **Ping**: A simple test event sent during webhook setup to confirm the endpoint is functional. Payload contains a `message`.
## Working with Webhook Events
To start using webhooks:
1. **Register a Webhook Endpoint**: Provide a URL endpoint where Coalesce Quality can send events. Use Settings > Integrations > Add integration > Webhook.
2. **Handle Incoming Events**: Set up your server to process incoming POST requests. Ensure that your endpoint correctly interprets the payload format defined in the webhook event schema. Return 2xx status codes to confirm receipt.
3. **Verify the Signature** (recommended): Confirm each request genuinely came from Coalesce Quality before acting on it. See [Verifying Webhook Signatures](#verifying-webhook-signatures).
4. **Automate Actions**: Use the event data to trigger specific actions in your application, such as updating a database, notifying users, or calling other APIs.
5. **Check what arrived**: every delivery is recorded with the request sent, the response returned and — when nothing was sent — the reason. See [Debugging Deliveries](/api-reference/webhook-deliveries).
### Custom request headers
You can attach custom HTTP headers to every webhook request under Settings > Integrations > your webhook integration — for example an `Authorization` header so Coalesce Quality can reach a protected endpoint.
Mark a header **secret** to store its value securely instead of in the plaintext configuration: secret header values are write-only — masked in the UI and API responses and never returned — and are sent only in the outgoing request. Non-secret headers keep a visible, editable value. To change a secret header enter a new value; leave it blank to keep the stored one.
For authenticating that a request genuinely came from Coalesce Quality, prefer [signature verification](#verifying-webhook-signatures) over a static token in a custom header.
## Verifying Webhook Signatures
Every webhook delivery is signed so your endpoint can verify that the request genuinely originated from Coalesce Quality and was not tampered with or replayed. Verification is strongly recommended — it is the most robust way to authenticate incoming webhooks (stronger than a static bearer token in a custom header).
### Signing secret
Each webhook integration has its own signing secret, generated by Coalesce Quality. The secret is **shown once**, at the moment it is generated — when you create the webhook integration and when you rotate it. Copy it then and store it somewhere secure; it is not retrievable afterwards. The value is prefixed with `whsec_`; use the entire string, including the prefix, as the HMAC key.
If you lose the secret, **rotate** it (Settings > Integrations > your webhook integration) to obtain a fresh one.
**Rotating without downtime**: when you rotate, the previous secret stays valid for a 24-hour grace window. During that window every delivery is signed with *both* the new and previous secrets (two values in `X-Coalesce-Signature`), so your endpoint keeps verifying whether it holds the old or the new secret. Update your endpoint to the new secret any time within the window.
To confirm your endpoint is on the new secret without waiting for a real event, replay an earlier delivery: a replay is signed with the current secret only. See [Redeliver](/api-reference/webhook-deliveries#redeliver).
### Signature headers
Two headers are sent on every request:
| Header | Description |
| - | - |
| `X-Coalesce-Timestamp` | Unix timestamp (seconds) of when the delivery was signed. |
| `X-Coalesce-Signature` | One or more space-separated, scheme-prefixed signatures — e.g. `v1=` or, during a rotation grace window, `v1= v1=`. A request is authentic if **any** `v1=` value matches. The `v1=` prefix identifies the signature scheme. |
### Signature scheme
Each signature is a hex-encoded HMAC-SHA256 computed over the timestamp and the raw request body, joined by a `.`:
```
signed_payload = "{X-Coalesce-Timestamp}." +
signature = hex( HMAC_SHA256(key = signing_secret, msg = signed_payload) )
```
To verify a request:
1. Read the `X-Coalesce-Timestamp` header and the **raw** request body (verify before any JSON parsing or re-serialization — a re-serialized body will not match).
2. Recompute the signature with your copy of the signing secret and check it against **each** `v1=` value in `X-Coalesce-Signature` using a **constant-time** comparison; accept the request if any matches.
3. Optionally reject requests whose timestamp is outside your tolerance window. Retries reuse the original signing timestamp, so allow for the retry backoff window (deliveries may be retried for up to \~30 minutes).
### Examples
```python Python theme={null}
import hmac
import hashlib
def verify(signing_secret: str, timestamp: str, raw_body: bytes, signature_header: str) -> bool:
signed_payload = timestamp.encode() + b"." + raw_body
expected = hmac.new(signing_secret.encode(), signed_payload, hashlib.sha256).hexdigest()
# signature_header is one or more space-separated "v1=" values;
# accept the request if any of them matches.
for token in signature_header.split():
scheme, _, provided = token.partition("=")
if scheme == "v1" and hmac.compare_digest(expected, provided):
return True
return False
# In your handler (e.g. Flask): pass request.headers and request.get_data() (raw bytes)
```
```javascript Node.js theme={null}
const crypto = require("crypto");
function verify(signingSecret, timestamp, rawBody, signatureHeader) {
const signedPayload = `${timestamp}.${rawBody}`;
const expected = crypto
.createHmac("sha256", signingSecret)
.update(signedPayload)
.digest("hex");
// signatureHeader is one or more space-separated "v1=" values;
// accept the request if any of them matches.
return signatureHeader.split(" ").some((token) => {
const [scheme, provided] = token.split("=");
return (
scheme === "v1" &&
provided &&
provided.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided))
);
});
}
// rawBody must be the unparsed request body (e.g. express.raw()), not a re-stringified object.
```
## Sample Webhook Event Payload
Here's an example of a webhook event payload for an issue created:
```json theme={null}
{
"workspace": "example_workspace",
"event_id": "0b6f9c2e-1a2b-4c3d-9e8f-123456789abc",
"event_time": "2026-07-16T12:34:56Z",
"event_type": "EVENT_TYPE_ISSUE_CREATED",
"issue_created": {
"issue": {
"issue_id": "789",
"issue_group_id": "group1",
"issue_url": "https://app.synq.io/incident/issues/group1?issueId=789",
"title": "Freshness check failed for orders table",
"description": "Table has not been updated in the last 2 hours\n\nOwned by: [Data Platform](https://app.synq.io/owners-v2/owner-a1b2c3d4-e5f6-7890-abcd-ef1234567890)",
"description_html": "
Table has not been updated in the last 2 hours
",
"trigger_entity": {
"name": "orders",
"type_name": "Clickhouse Table",
"identifier": {
"clickhouse_table": { "host": "prod", "schema": "analytics", "table": "orders" }
},
"entity_url": "https://app.synq.io/catalog/ch-prod::analytics::orders"
},
"monitored_entities": [
{
"name": "orders",
"type_name": "Clickhouse Table",
"identifier": {
"clickhouse_table": { "host": "prod", "schema": "analytics", "table": "orders" }
},
"entity_url": "https://app.synq.io/catalog/ch-prod::analytics::orders"
}
],
"started_at": "2026-07-16T12:34:56Z",
"trigger_run_id": "freshness-orders-2026-07-16T12:34:56Z",
"trigger_name": "Freshness monitor",
"trigger_message": "Table has not been updated in the last 2 hours",
"status": "ISSUE_STATUS_INVESTIGATING",
"owners": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Data Platform",
"url": "https://app.synq.io/owners-v2/owner-a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
]
}
}
}
```
This example is illustrative. The [JSON Schema](https://schemas.synq.io/webhook/v1/event.schema.json) is the authoritative, always-current field reference — code against it for the complete set of fields on each event type. `owners` is present on issue events (`issue_created`, `issue_updated`, `issue_status_updated`, `issue_closed`) and lists the owners of the affected entities, each with an `id` (matching the Owners API), a display `title`, and a deep-link `url`. It is omitted when the affected entities have no owner assigned. New fields are added in a backward-compatible way.
# Debugging Deliveries
Source: https://docs.synq.io/api-reference/webhook-deliveries
See what Coalesce Quality sent to your webhook, PagerDuty or Opsgenie integration, why a notification did not arrive, and replay it
# Deliveries
Every notification Coalesce Quality sends to one of your integrations is recorded: the request as it was sent, the response your endpoint returned, how long it took, and — when nothing was sent at all — the reason. Open it under **Settings > Integrations > your integration > Deliveries**.
The tab is available for every integration that receives notifications: **webhooks**, **PagerDuty** and **Opsgenie**. Everything below applies to all three, and everything visible there is also readable over the API — see [Over the API](#over-the-api).
Deliveries are kept for **90 days**.
## What a delivery is
One **delivery** is one event addressed to one integration.
An event that matches three integrations produces three deliveries. They share an **event id** and each has its own delivery id. The event id is the same `event_id` your endpoint received in the webhook payload, so you can go from a payload you have in hand to the delivery that produced it — and to every other integration that got the same event.
Each delivery has one or more **attempts**. An attempt is one HTTP request and whatever came back.
## Outcomes
| Outcome | What it means | What to do |
| - | - | - |
| **Delivered** | Your endpoint accepted it. Only a `2xx` counts. | Nothing. |
| **Pending** | Accepted and queued, with an attempt still to come. The row shows when the next attempt is due. | Wait. If it stays pending, check that your endpoint is reachable. |
| **Failed** | Your endpoint rejected it outright, or it was still failing when the retries ran out. Nothing further will be attempted. | Read the attempts, fix the endpoint, then **Redeliver**. |
| **Skipped** | The integration was found and its own configuration meant nothing was sent. | See [Why nothing was sent](#why-nothing-was-sent). |
| **Not routed** | No integration could be resolved for the event. | See [Why nothing was sent](#why-nothing-was-sent). |
A status code is not an outcome. A `500` that will be retried and a `500` that exhausted the retries are the same status and different outcomes, which is why the outcome is what to filter and alert on.
### Retries
A failing delivery is retried up to **10 times** with exponential backoff, over roughly 30 minutes.
* Only a `2xx` response counts as delivered.
* A `400`, `404` or `422` is treated as permanent and not retried: those say the request itself is wrong, and repeating it will not change the answer.
* A `5xx`, a timeout and a connection failure are retried.
* A `3xx` is **not followed** and is retried like any other transient failure. A delivery can carry credentials scoped to one host, so following a redirect could send them somewhere else. Point the integration at the final URL rather than at something that redirects.
Retries reuse the original signing timestamp, so a timestamp tolerance in your [signature verification](/api-reference/webhook#verifying-webhook-signatures) has to allow for the retry window.
## Why nothing was sent
A delivery that sent nothing always records a reason. Each one names something specific to change.
| Reason | What it means | The fix |
| - | - | - |
| **Event type not subscribed** | The integration does not list this event type. | Add it under the integration's event types. |
| **Event type unsupported by integration** | The destination has no representation for this kind of event. PagerDuty and Opsgenie carry issue and incident lifecycle events, not every webhook event type. | Nothing to fix. Use a webhook integration if you need this event type. |
| **No action for lifecycle state** | The change of state maps to no action on the destination — for example a status change that PagerDuty or Opsgenie has no equivalent for. | Nothing to fix. |
| **Integration not found** | The integration the event was addressed to no longer exists. | Expected shortly after deleting an integration. Otherwise contact support. |
| **Integration disabled** | The integration exists but is switched off. | Enable it. |
| **Empty event subject** | The event carried no issue or incident to describe. | Contact support. |
This is the answer to "my alerting has gone quiet". A workspace whose deliveries are all **Skipped** with *Event type not subscribed* is configured to send nothing, which looks identical to a broken endpoint until you read the reason.
Deliveries describe what happened **after** an event was routed to an integration. An alert that was suppressed before that, or that matched no ownership, produces no delivery at all — so an event you expected and cannot find here is a question about the alert configuration, not about the integration. Each delivery names the alert configuration that selected it, which is where to start.
## Reading a request and a response
Expand a delivery to see each attempt: the method and URL, the request headers and body, the response status, headers and body, and the duration.
### Withheld header values
A header whose value carries a credential is reported by name but its value is never returned. That covers:
* headers you marked **secret** on the integration,
* credentials Coalesce Quality holds for the destination, such as the Opsgenie API key,
* the standard credential-bearing names — `Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`, `X-Api-Key`, `X-Auth-Token` — whatever they contain, on the request **or** the response side.
A withheld header shows a **fingerprint** instead: a short digest, stable for the same value. You cannot read the value from it, but you can compare two deliveries and tell whether the value changed — which is usually the actual question ("is this the same token as the one that worked yesterday"). Alongside it, the **secret version** says which stored secret the value was read from, so a delivery stays attributable across a rotation.
The header is still listed. A missing value means withheld, never "no header was sent".
### Signature headers are shown in full
`X-Coalesce-Signature` and `X-Coalesce-Timestamp` are returned complete. The signature is a digest of the body and the timestamp is a timestamp; neither is your signing secret, and reproducing a verification that failed needs the exact bytes. See [Verifying Webhook Signatures](/api-reference/webhook#verifying-webhook-signatures).
### Bodies
A request body value the destination requires to be secret — a PagerDuty routing key, for instance — is replaced by a marker naming the field it stood in, so the record shows a value was sent rather than implying none was. The reported request size is the real one, before that substitution.
A response body is kept up to a fixed size and marked as truncated past it, with the full size reported. The first error line is where the diagnostic value is.
## Redeliver
**Redeliver** sends a stored event again, to the same integration, with the same payload. The replay is a new delivery with its own id, linked to the original, and it appears in the feed like any other.
It is signed with the integration's **current** signing secret, not the one the original used. That makes it the way to confirm a [secret rotation](/api-reference/webhook#signing-secret) reached your receiver: rotate the secret, update your endpoint, redeliver an earlier event, and a **Delivered** outcome proves the new secret is accepted. There is no need to wait for a real alert, and no need to wait out the 24-hour dual-signing window to find out.
Redeliver is rate-limited per integration, so a replay loop cannot hammer an endpoint. Over the limit it is refused rather than queued.
## Send test event
**Send test event** delivers a test payload and reports the real outcome, rather than only that it was sent.
It is always sent, whatever event types the integration subscribes to. A test that silently matched no subscription would report exactly what a broken endpoint does, and telling those two apart is the only reason to run a test. On Opsgenie it opens a real alert, aliased so repeated tests do not stack up, because that is the only thing the integration does.
## Over the API
Everything above is available over the public API. Reading needs a token with **Read Deliveries** (`SCOPE_DELIVERY_READ`); replaying and testing need **Send and Replay Deliveries** (`SCOPE_DELIVERY_EDIT`). Create a token under [API settings](https://app.synq.io/settings/api) and get an access token as described in [Getting Started](/api-reference/getting-started).
The examples use the EU endpoint. For other regions use `api.us.synq.io` or `api.au.synq.io`.
### List deliveries
```bash theme={null}
curl -G https://developer.synq.io/api/deliveries/v1 \
-H "Authorization: Bearer $TOKEN" \
-d integrationId=d577b364-a867-11ed-b4b2-fe8020e7ba25 \
-d since=2026-08-01T00:00:00Z \
-d pagination.pageSize=50
```
Deliveries come back newest event first. Pass the response's `pageInfo.lastId` as `pagination.cursor` for the next page; an empty `lastId` means there are no more.
Omit `integrationId` for the whole workspace's feed. Add `includeRouting=true` to get the alert configurations that selected each integration — it is off by default because it costs an extra read per delivery.
### Filter by outcome
```bash theme={null}
curl -G https://developer.synq.io/api/deliveries/v1 \
-H "Authorization: Bearer $TOKEN" \
-d integrationId=d577b364-a867-11ed-b4b2-fe8020e7ba25 \
-d outcomes=OUTCOME_FAILED \
-d since=2026-08-01T00:00:00Z
```
`outcomes` and `statusCodes` repeat, so `-d outcomes=OUTCOME_FAILED -d outcomes=OUTCOME_SKIPPED` matches either. `eventTypes` filters by event type name, and `eventId` narrows to a single event across every integration it reached.
### Find every integration one event reached
```bash theme={null}
curl -G https://developer.synq.io/api/deliveries/v1 \
-H "Authorization: Bearer $TOKEN" \
-d eventId=0b6f9c2e-1a2b-4c3d-9e8f-123456789abc
```
`eventId` is the `event_id` from the webhook payload your endpoint received.
### Fetch the attempts of a delivery
```bash theme={null}
curl https://developer.synq.io/api/deliveries/v1//attempts \
-H "Authorization: Bearer $TOKEN"
```
Oldest attempt first. A delivery is attempted at most ten times, so the result is small and is not paginated. It is empty for a delivery that sent nothing, and for one still waiting on its first attempt.
The `` is the `id` of a delivery from the list — an opaque value composed by the service. Pass it back as it came; do not build one yourself.
### Replay a delivery
```bash theme={null}
curl -X POST https://developer.synq.io/api/deliveries/v1//redeliver \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -d '{}'
```
The response carries the new delivery's id. The send is asynchronous, so poll for the outcome until it leaves `OUTCOME_PENDING`:
```bash theme={null}
curl -X POST https://developer.synq.io/api/deliveries/v1/by-id \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": [""]}'
```
An id that does not exist, or that has passed out of the 90-day window, is absent from the response rather than an error.
### Send a test delivery
```bash theme={null}
curl -X POST https://developer.synq.io/api/deliveries/v1/test \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"integrationId": "d577b364-a867-11ed-b4b2-fe8020e7ba25", "message": "checking the new endpoint"}'
```
Returns the id of the delivery it created; poll it the same way.
### Watch your own delivery failure rate
```bash theme={null}
curl -X POST https://developer.synq.io/api/deliveries/v1/summarise \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"integrationId": "d577b364-a867-11ed-b4b2-fe8020e7ba25", "since": "2026-08-12T00:00:00Z"}'
```
Counts the window by outcome, by skip reason and by HTTP status class, without paging through the feed. This is the call to alert on if you want to know about a broken receiver before someone notices a missing alert.
A full worked example in Go — list, filter, attempts, redeliver and summarise — is on the [examples](/api-reference/examples) page as `deliveries`.
## Related
* [Using Webhooks](/api-reference/webhook) — event types, payload schema, custom headers and signature verification.
* [PagerDuty](/alert-integrations/pagerduty) and [Opsgenie](/alert-integrations/opsgenie) — setting those integrations up.
* [API Scopes](/api-reference/scopes) — the full scope reference.
# Looker
Source: https://docs.synq.io/bi-integrations/looker
Integrating Looker with Coalesce Quality
This guide will walk you through how to set up the Coalesce Quality integration with Looker. We connect to Looker via API (to fetch the structure of your Looker objects) and via git (to fetch lookML) models. As a result, we can create a complete picture of your Looker setup and connect it to your data warehouse and transformation models (dbt, SQLMesh, or Coalesce Transform).
To finish this guide, you’ll need the following:
→ Admin permissions for Looker
→ Access to GitHub or GitLab
→ Admin Access to your Looker repository (only repo admins can add deploy keys)
⏱️ Estimated time to finish: 15 minutes.
# Integration
The optimal way to setup is an API key with admin role permissions, which would give Coalesce Quality the necessary permissions to fetch all observability data to reconstruct end-to-end lineage of your Looker objects.
*In case you are limited by security policies to provide Admin-level access, we also offer an admin-less integration using a CLI tool. Reach out to us in our shared Slack channel to set this up.*
# Setup guide
Follow the Looker access and LookML access guides below to set up the Looker integration.
## Looker access
To process your Looker data, we need access to your Looker instance. Coalesce Quality connects to Looker with `clientId` and `clientSecret`, which is bound to a user with Admin permissions.
1. Go to `Admin > Users` and create a new user with `Add User`
1. Use `synq@.com` as email address
2. Disable `Send setup emails` and Save
3. Give the user the `Admin` role
2. Edit the `synq@.com` user and create `clientId` and `clientSecret` API permissions (see [Looker integration docs](https://cloud.google.com/looker/docs/api-auth#authentication_with_an_sdk) for details).
1. click `Edit Keys`
2. click `New API Key`
3. In Coalesce Quality, you'll need your instance base URL (`https://.cloud.looker.com`), `clientId`, and `clientSecret` securely to complete the integration.
# LookML Access
To process LookML models, we need access to the underlying Looker repository. The Git integration has to be setup as a separate step and can be done in Coalesce Quality: [https://app.synq.io/settings/integrations/git](https://app.synq.io/settings/integrations/git). The process executes in two phases:
### 1. Collect information about your repo
1. **Repository URL**—The full URL of the git repository that contains your Looker project (we support both HTTPS and SSH URLs).
2. **Branches to analyze (default: `master` and `main`)**—By default, the `master` and `main` branches are analysed. Please provide the name if you prefer us to process the code from any other branch. We support multiple branches too.
3. **Path to analyze (default: `.`)**—By default, Coalesce Quality looks for your LookML source code in the root of the repository. If you store your code in a subdirectory, please provide us with a path if your Looker project uses a subdirectory. In most cases, it is best to leave this as the default to analyze everything in the repository.
### 2. Setup Deployment Key
To finish the setup, you will have to store the public part of the Coalesce Quality deployment key with your git provider. You can follow the instructions based on your git provider below:
* [GitHub - setup deploy key](https://synq-15.mintlify.app/code-integrations/github#setup-a-deploy-key)
* [GitLab - setup deploy key](https://synq-15.mintlify.app/code-integrations/gitlab#setup-a-deploy-key)
# Omni
Source: https://docs.synq.io/bi-integrations/omni
Integrating Omni with Coalesce Quality
This guide will walk you through how to set up the Coalesce Quality integration with Omni. As a result, we can create a complete picture of your Omni setup and connect it to your data warehouse and transformation models (dbt, SQLMesh, or Coalesce Transform).
⏱️ Estimated time to finish: 10 minutes.
# Integration
Get in touch with your Solutions Architect to set up the Omni integration in your Coalesce Quality workspace
# Tableau
Source: https://docs.synq.io/bi-integrations/tableau
Integrating Tableau with Coalesce Quality
This guide will walk you through how to set up the Coalesce Quality integration with Tableau. As a result, we can create a complete picture of your Tableau setup and connect it to your data warehouse and transformation models (dbt, SQLMesh, or Coalesce Transform).
To finish this guide, you’ll need the following:
→ Site Administrator, Site Administrator Explorer or Site Administrator Creator access to Tableau
⏱️ Estimated time to finish: 10 minutes.
# Integration
Coalesce Quality connects to your Tableau instance as a specific user, authenticated via [personal access token](https://help.tableau.com/current/server/en-us/security_personal_access_tokens.htm). The optimal way to set it up is to create a personal access token under at least `Site Administrator Explorer` user, which would give Coalesce Quality the necessary permissions to fetch all observability data to reconstruct the end-to-end lineage of your Tableau objects.
*In case you are limited by security policies to provide Admin-level access, we also offer an admin-less integration using a CLI tool. Reach out to us in our shared Slack channel to set this up.*
## Setup guide
### Connection URI
The base URL of your Tableau cloud instance (e.g., `https://dub01.online.tableau.com`)
### Site
The site for your Tableau project. You can find this after the `/site/` path in the URL (e.g., `synq` in `https://dub01.online.tableau.com/#/site/synq/`)
### Token name and token secret
Personal Access Tokens (PATs) are disabled by default. To enable them, log into Tableau as a site administrator, go into `Settings` (not `My Account Settings`), and scroll to the Personal Access Tokens section. Hit `save` to apply the changes.
Once you’ve enabled the creation of Personal Access Tokens, go to `My Account Settings` scroll down to Personal Access Tokens, and click `Create Token`. Copy the `token name` and `secret` and input them into Coalesce Quality.
### Projects
By default, we’ll ingest all your Tableau projects. If you only want some projects integrated, add the names of those in Coalesce Quality
# Atlan
Source: https://docs.synq.io/catalogs/atlan
Integrating Atlan with Coalesce Quality
Coalesce Quality has a deep bi-directional integration with Atlan, helping you manage data without duplicating effort across both platforms. Define data products and ownership in Atlan, then add monitoring and alerting rules in Coalesce Quality. The resulting quality score is visible to users on either platform, making the entire ecosystem transparent and seamless.
> Reach out to the Coalesce Quality team to enable Atlan integration for your workspace.
Once enabled, you can add an Atlan integration from the `Integrations` tab on the `Settings` page.
You then need to add the following information:
1. Tenant URL - This is the URL you use to access Atlan (eg. `https://.atlan.com`)
2. API token - Generated as following.
To generate the API token, head over to Atlan.
* In `Governance > Personas` , create a new persona. Name it something indicative like `Coalesce Quality - API Access`.
* Within the persona, create the following policies:
* `Domain Policy` with `Read` permission for domains. You can choose `All Domains` or cherry pick the ones you want to be visible in Coalesce Quality.
* `Metadata Policy` for each connection that you want to be visible in Coalesce Quality. Choose the permission `Assets -> Read`
* In `Admin > API Tokens` create a new API token. Name it something indicative like `Coalesce Quality - API`.
* Choose `Expiry Never`
* Add the persona you just created in the step above (`Coalesce Quality - API Access`)
* Download or copy the API token that you see.
Enter the Tenant URL and API token in the corresponding section of the Atlan integration.
If the URL and Token are valid you will be able to proceed to the next steps.
1. Map supported integrations with Atlan connections.
2. Choose which Atlan assets to synchronize.
The Atlan integration synchronizes every 30 minutes and performs a full catalog sync along with the domains, dataproducts and owners you selected. You can easily identify the assets pulled from Atlan by the logo next to them.
You can enhance the Owners with your monitoring and alerting preferences.
Reach out in our shared Slack channel for any assistance you might need with your Atlan integration.
# Command-line tools
Source: https://docs.synq.io/cli
Install any of the Coalesce Quality CLIs, and find the reference for each
Three command-line tools, all of them installable and runnable on your own
infrastructure without involving us. They share credentials, so one login covers
all three.
| Tool | What it does | Guides |
| - | - | - |
| **`synqcli`** | Declares monitors, SQL tests and deployment rules as code, and reconciles a workspace with that declaration | [Monitors as code](/monitors/monitors-as-code) · [Agent workflow](/monitors/agent-workflow) · [CLI reference](/monitors/cli) |
| **`synq-recon`** | Compares a dataset in one database against another and reports whether they agree, without moving row data | [Overview](/reconciliation/overview) · [Getting started](/reconciliation/getting-started) · [Agent workflow](/reconciliation/agent-workflow) · [CLI reference](/reconciliation/cli) |
| **`synq-scout`** | Runs the Scout agent, or serves its tools locally over MCP | [Scout](/scout/scout) · [Agent workflow](/scout/agent-workflow) · [CLI reference](/scout/cli) |
Each tool ships an **`AGENTS.md`** beside the binary — the operating guide,
written for a coding agent to read top-down and act on. It is the same content as
the "Agent workflow" page linked above, so an agent with a download and no web
access still has it. If you are pointing an agent at one of these tools, that is
the file to give it.
## Install
On macOS, install any of them with Homebrew, and upgrade with `brew upgrade`:
```bash theme={null}
brew install getsynq/tap/synqcli
brew install getsynq/tap/synq-recon
brew install getsynq/tap/synq-scout
```
Homebrew owns the binary once it installs it, so ` upgrade` will point you
back to `brew upgrade` rather than replace it. Either way you have an upgrade
path — see [Upgrade](#upgrade).
Homebrew serves macOS only. Everywhere else — Linux, CI, Windows — take the
archives. Every release publishes them for macOS and Linux on both `amd64` and
`arm64` (`synqcli` also builds for Windows on both, `synq-recon` on `amd64`), plus
a `checksums.txt`.
The archive filename carries the version, so it has to be named explicitly. Rather
than transcribe one, resolve it: GitHub redirects `/releases/latest` to the tag of
the newest release, so reading that redirect gives you the current version with no
API token and no rate limit. It is the same lookup each tool's own
[`upgrade`](#upgrade) uses.
```bash theme={null}
VERSION=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
https://github.com/getsynq/synqcli/releases/latest | sed 's#.*/v##')
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
curl -fL "https://github.com/getsynq/synqcli/releases/download/v${VERSION}/synqcli_${VERSION}_${OS}_${ARCH}.tar.gz" \
| tar -xz
sudo mv synqcli /usr/local/bin/
synqcli --version
```
On Windows, download the `.zip` for your architecture from the
[releases page](https://github.com/getsynq/synqcli/releases) and put
`synqcli.exe` on your `PATH`.
```bash theme={null}
VERSION=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
https://github.com/getsynq/synq-recon/releases/latest | sed 's#.*/v##')
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
curl -fL "https://github.com/getsynq/synq-recon/releases/download/v${VERSION}/synq-recon_${VERSION}_${OS}_${ARCH}.tar.gz" \
| tar -xz
sudo mv synq-recon /usr/local/bin/
synq-recon --version
```
On Windows, download `synq-recon__windows_amd64.zip` from the
[releases page](https://github.com/getsynq/synq-recon/releases) and put
`synq-recon.exe` on your `PATH`. It runs DuckDB like the other builds.
Also available as a container image, which is the quickest way to try it with no
warehouse at all — the walkthrough in
[Getting started](/reconciliation/getting-started) runs against DuckDB:
```bash theme={null}
docker pull europe-docker.pkg.dev/synq-cicd-public/synq-public/synq-recon:latest
```
```bash theme={null}
VERSION=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
https://github.com/getsynq/synq-scout/releases/latest | sed 's#.*/v##')
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
curl -fL "https://github.com/getsynq/synq-scout/releases/download/v${VERSION}/synq-scout_${VERSION}_${OS}_${ARCH}.tar.gz" \
| tar -xz
sudo mv synq-scout /usr/local/bin/
synq-scout --version
```
For a long-running deployment, use the Kubernetes manifests in
[getsynq/synq-scout-k8s](https://github.com/getsynq/synq-scout-k8s) — see
[Scout on-premise setup](/scout/on-premise).
You do **not** need this binary to use Scout's tools from an AI client: the MCP
server is hosted. See [MCP](/scout/mcp).
Verify a download against the checksums published with the release:
```bash theme={null}
sha256sum -c checksums.txt --ignore-missing
```
On macOS a binary downloaded this way may be quarantined. If it refuses to start,
`xattr -d com.apple.quarantine /usr/local/bin/` clears the flag. The Homebrew
install does this for you.
## Upgrade
A tool installed from an archive can replace itself:
```bash theme={null}
synqcli upgrade --check # what it would do, without doing it
synqcli upgrade
```
`upgrade` resolves the latest release, downloads the archive for your platform,
verifies it against the release's `checksums.txt`, and runs the new binary once to
confirm it works before replacing anything. If the binary lives somewhere you
cannot write, it says so and changes nothing — re-run with `sudo`.
An install owned by something else is left to its owner: a Homebrew install tells
you to run `brew upgrade`, and a container or Kubernetes deployment should take a
newer image rather than replace the binary inside it.
Each tool also mentions a newer release on stderr, at most once a day. That check
reads a tag from a public GitHub URL and sends nothing but the tool name and
version — no credentials, no workspace, no identity. It never delays the command it
runs beside and never reports its own failure, so a machine with no route to the
internet behaves exactly like one that is up to date. It is already silent in CI,
when output is not a terminal, and inside a container or a Kubernetes pod. To turn
it off everywhere:
```bash theme={null}
export QUALITY_NO_UPDATE_CHECK=1 # DO_NOT_TRACK=1 has the same effect
```
## Sign in
All three resolve credentials the same way, and take the first they find:
1. **Client credentials** — `QUALITY_CLIENT_ID` + `QUALITY_CLIENT_SECRET`, for
servers, containers and CI. Create the pair under Settings → API.
2. **An API token** — `QUALITY_TOKEN`, the `st-…` token from your workspace.
3. **A browser login** — ` auth login`, which caches a refresh token under
`~/.synq/oauth/`.
The cache is shared, so one `auth login` covers all three for the same deployment.
```bash theme={null}
synqcli auth login
synqcli auth status # every stored credential, for every region
```
If your workspace is not in the EU, add `--region us` (or `au`), or set
`QUALITY_REGION`. The flag works on every command, and a successful login is
remembered, so later commands need neither it nor `--endpoint`.
Confirm which workspace you are pointed at before running anything that writes.
`synq-scout auth whoami` and `synqcli auth status` both print it. A `synqcli
deploy` against the wrong region reconciles the wrong workspace — and a reconcile
deletes what it does not find declared.
## Get the command reference
Each tool's full command and flag reference is generated from the tool itself, so it
always matches the release you have:
* [`synqcli` reference](/monitors/cli)
* [`synq-recon` reference](/reconciliation/cli)
* [`synq-scout` reference](/scout/cli)
`--help` on any command prints the same content locally, and every tool supports
shell completion.
## Field references for the YAML
Each tool's configuration schema is published and versioned, and each is
authoritative in a way prose is not. Two forms of the same thing: a rendered page to
read, and the JSON Schema to point an editor at.
| Tool | Configures | Reference | Schema URL |
| - | - | - | - |
| `synqcli` | monitors, tests, deployment rules | [config.html](https://schemas.synq.io/synq-monitors/v1/config.html) | `https://schemas.synq.io/synq-monitors/v1/config.schema.json` |
| `synq-recon` | suites, connections, comparisons | [config.html](https://schemas.synq.io/synq-recon/v1/config.html) | `https://schemas.synq.io/synq-recon/v1/config.schema.json` |
| `synq-recon` | the audit log a run writes | [audit-log.html](https://schemas.synq.io/synq-recon/v1/audit-log.html) | `https://schemas.synq.io/synq-recon/v1/audit-log.schema.json` |
| `synq-scout` | `agent.yaml` | [config.html](https://schemas.synq.io/synq-scout/v1/config.html) | `https://schemas.synq.io/synq-scout/v1/config.schema.json` |
Put the matching line at the top of your YAML and any editor with the YAML language
server gives completion and validation as you type:
```yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-recon/v1/config.schema.json
```
The [data-warehouse agent](/dw-integrations/agent) publishes its config schema the
same way.
## Use them from CI
Client credentials plus the archive download is all CI needs; no interactive login
is involved.
Pin the version in a pipeline — set `VERSION` to a literal instead of resolving
`/releases/latest` — so a release cannot change what your build runs without a
commit. On a workstation, take the latest. Either way check
` --version` into the build log; it is the first thing anyone asks when a
pipeline behaves differently from a laptop.
`synqcli deploy --auto-confirm` and `synq-recon`'s exit codes are the
two things worth reading before wiring a pipeline —
[Reconciliation in CI/CD](/reconciliation/cicd-and-automation) covers the second in
full.
Need something not covered here? [Get in touch](/support/support).
# Coalesce Catalog
Source: https://docs.synq.io/coalesce-integrations/coalesce-catalog
Connect Coalesce Quality with your Catalog workspace for bidirectional metadata sync
This guide will walk you through how to connect Coalesce Quality with Coalesce Catalog (formerly Castordoc). The integration allows bidirectional metadata synchronization between the two platforms.
To finish this guide, you'll need the following:
→ Admin access to your Coalesce Quality workspace
→ Admin access to your Coalesce Catalog workspace
→ At least one data warehouse integration configured in the platform (for warehouse mappings)
⏱️ Estimated time to finish: 15 minutes.
# Integration
Coalesce Catalog is a data catalog solution that helps organizations discover, understand, and govern their data assets. This integration connects your Catalog and quality workspaces to provide a unified view of your data assets and their health.
The integration is **bidirectional**:
**From Catalog to quality:**
* Pulls all catalog entities (tables, dashboards, etc.) and maps them to corresponding quality assets
* This ensures that assets in both platforms are linked, providing a unified view across your data stack
**From quality to Catalog:**
* Reports monitor status, test results, and asset health as **Data Qualities** in your catalog
* Supports both **table-level** and **column-level** quality reporting
* Keeps your catalog enriched with real-time data quality information
# Setup guide
## Name your integration
For example `Coalesce Catalog` or `Production Catalog`
## Select the region
Select your Coalesce Catalog region from the dropdown:
* **Rest of World (EU):** [https://app.castordoc.com/](https://app.castordoc.com/)
* **United States:** [https://app.us.castordoc.com/](https://app.us.castordoc.com/)
Choose the region that matches your Coalesce Catalog workspace. You can determine your region by looking at the URL you use to access Coalesce Catalog.
## Generate a token
Coalesce Catalog API tokens are managed by your organization's Catalog ops team
1. Contact your **Coalesce Catalog ops team** to request an API token
2. Ensure the token has appropriate read and write permissions for metadata synchronization
3. Store the token securely in a secrets manager — never commit tokens to version control
Store your API token securely. Coalesce Catalog tokens provide access to your workspace metadata and should be treated as sensitive credentials.
## Configure sync intervals
### Fetch interval (hours)
How often the platform should fetch catalog entities from Coalesce Catalog. Default: **2 hours**
This controls how frequently the platform pulls tables, dashboards, and other catalog entries to map them with your quality assets.
### Upload interval (minutes)
How often the platform should upload Data Qualities to your Catalog workspace. Default: **30 minutes**
This controls how frequently the platform pushes monitor status, test results, and asset health information to your catalog entries.
## Configure warehouse mappings
Warehouse mappings connect your Catalog warehouses to your data warehouse integrations. This enables the platform to correlate assets between both systems.
You must have at least one data warehouse integration (BigQuery, Snowflake, Databricks, etc.) configured before you can add warehouse mappings.
To add a warehouse mapping:
1. Click **Add Warehouse Mapping**
2. Select a **Coalesce Warehouse** from the dropdown (or enter the warehouse ID manually if not listed)
3. Select the corresponding **Data Warehouse** integration
4. The **Mapping Name** is auto-generated based on your selections, but you can customize it
You can add multiple warehouse mappings to connect all your Catalog warehouses with their corresponding data warehouse integrations.
# What data is synced?
## Catalog entities (Catalog → quality)
The platform pulls catalog entities and maps them to corresponding quality assets:
* Tables and views
* Dashboards and reports
* Other catalog entries
This mapping ensures that when you view an asset in either platform, it's linked to its counterpart, providing a unified experience.
## Data Qualities (quality → Catalog)
The platform uploads data quality information to your catalog as **Data Qualities**, which appear on your catalog entries:
**Table-level qualities:**
* Monitor status (passing, failing, warning)
* Test results from transformation layer tests (dbt, SQLMesh, Coalesce Transform) and custom SQL tests
* Freshness and volume health indicators
* Overall asset health status
**Column-level qualities:**
* Column-specific monitor results
* Field-level test outcomes
* Column health indicators
This enables your data consumers to see quality status directly in the catalog without needing to switch platforms.
# FAQ
### What access do you require?
We require write access to Data Qualities via the Coalesce Catalog Public API. The token also needs read access to list available warehouses during configuration. We don't access raw data in your data warehouse through this integration.
### What if I change regions?
If you change the region, you must provide a new API token for that region. Tokens are region-specific and cannot be used across different Coalesce Catalog regions.
### What if warehouses don't appear in the dropdown?
If warehouses don't load:
1. Verify your API token is correct and has read permissions
2. Ensure the region matches your Coalesce Catalog workspace
3. Check that warehouses are configured in your Coalesce Catalog
4. You can manually enter the warehouse UUID if the dropdown doesn't populate
### How are Data Qualities displayed in the catalog?
Data Quality entries appear on your table and column pages in Coalesce Catalog. These show the current status of monitors and tests, allowing catalog users to quickly assess data health.
# Coalesce Transform
Source: https://docs.synq.io/coalesce-integrations/coalesce-transform
Connect Coalesce Quality with your Transform workspace for pipeline monitoring and lineage
This guide will walk you through how to connect your Coalesce Transform workspace to Coalesce Quality, enabling you to track pipeline runs, job statuses, and metadata changes.
To finish this guide, you'll need the following:
→ Admin access to your Coalesce Quality workspace
→ Admin access to your Coalesce Transform workspace
→ Ability to generate API tokens in Coalesce
⏱️ Estimated time to finish: 10 minutes.
# Integration
[Coalesce](https://coalesce.io) is a metadata-driven data transformation platform that helps data teams build and operate governed data pipelines on cloud data platforms. It combines visual DAG-based design with code editing capabilities, built-in governance, and column-level lineage tracking.
This integration uses read-only APIs to fetch metadata from Coalesce. It cannot modify your projects, configurations, or pipelines.
Integrating Coalesce Transform gives you:
## Transformation Catalog & Discovery
See your entire Coalesce landscape in one place:
* **Projects** with their Git repository connections
* **Environments** (dev, staging, prod) with their configurations
* **Nodes** (transformations) - what each one does, where it writes data
* **Jobs** - scheduled transformation groups
## Run Monitoring & Alerting
Every transformation execution is tracked:
* **Deploy runs** - when new transformation code is deployed
* **Refresh runs** - when scheduled jobs execute
* **Per-query details** - which SQL ran, how long it took, rows affected
Status tracking includes success/failure, test failures (blocking vs non-blocking), error messages, and who triggered the run.
## Data Lineage
**Table-level lineage:**
* Which Coalesce nodes feed into which downstream nodes
* Connection to physical warehouse tables (Snowflake, BigQuery, etc.)
**Column-level lineage:**
* Which source columns flow into which target columns
* Whether columns are passed through, renamed, or transformed
## Data Quality Test Results
Coalesce SQL tests are tracked as part of runs:
* Node-level tests (custom SQL assertions)
* Column-level tests (null checks, uniqueness, etc.)
* Pass/fail status per test
* Whether failures block the pipeline or just warn
## SQL Visibility
The actual SQL executed by Coalesce is captured:
* Pre-SQL (setup statements)
* Main transformation query
* Post-SQL (cleanup/grants)
* Test queries
## Cross-Platform Connectivity
Coalesce nodes are linked to:
* Their physical warehouse tables (identity relationship)
* Git files that define them
* Downstream BI tools that query the output tables
This provides true end-to-end lineage from source → Coalesce transformation → warehouse table → dashboard.
# Setup guide
## Name your integration
For example `Coalesce Transform` or `Production Coalesce`
## Select the region
Select your Coalesce server region from the dropdown:
| Region | Endpoint |
| - | - |
| US primary | `https://app.coalescesoftware.io` |
| Europe primary | `https://app.eu.coalescesoftware.io` |
| Australia primary | `https://app.australia-southeast1.gcp.coalescesoftware.io` |
| US AWS US East | `https://app.us-east-1.aws.coalescesoftware.io` |
| US AWS US East with PrivateLink | `https://app.us-east-1.aws.privatelink.coalescesoftware.io` |
| US AWS US West | `https://app.us-west-2.aws.coalescesoftware.io` |
| US AWS US West with PrivateLink | `https://app.us-west-2.aws.privatelink.coalescesoftware.io` |
| Canada Primary | `https://app.northamerica-northeast1.gcp.coalescesoftware.io` |
| US Azure West 2 | `https://app.westus2.azure.coalescesoftware.io` |
| US Azure East 2 | `https://app.eastus2.azure.coalescesoftware.io` |
| Custom endpoint | Enter your own URL |
Choose the region that matches your Coalesce workspace. You can find your region by looking at the URL you use to access Coalesce.
If your Coalesce instance uses a custom domain or private deployment, select **Custom endpoint** and enter your Coalesce API URL. Must be an HTTPS URL.
## Generate a token
These steps will take you through how to create a token to grant read access to your Coalesce Transform project
1. Log into your Coalesce workspace
2. Navigate to the **Deploy** section
3. Click **Generate Access Token**
4. Copy the generated token immediately (you won't be able to view it again)
5. Paste it into the **API Token** field in the integration setup
Store your access token securely. Coalesce tokens provide access to your workspace metadata and should be treated as sensitive credentials. Copy the token immediately after generation as you won't be able to view it again.
Your Coalesce account must have API access enabled. The generated token will have the same access rights as the user who created it, so ensure you have access to the projects you want to sync.
## Configure fetch interval
Configure how often the platform should poll Coalesce Transform for updates. Default: **5 minutes**
You can adjust this based on:
* **Higher frequency (1-5 minutes):** For production environments where you need near real-time monitoring
* **Lower frequency (10-30 minutes):** For development environments or when API rate limits are a concern
## Select projects
After entering valid credentials, the platform will automatically load your available Coalesce Transform projects.
You can:
* **Select specific projects** by checking the boxes next to project names
* **Sync all projects** by leaving all checkboxes unchecked. This is useful when you want the platform to automatically include new projects as they're created in Coalesce Transform.
For each selected project, you can configure environment filtering:
* **All environments** - sync all environments in the project
* **Include specific environments** - only sync selected environments
* **Exclude specific environments** - sync all except selected environments
**Recommendation:** If you have many projects, start by selecting only production projects. You can always edit the integration later to include additional projects.
# Typical use cases
| Use Case | How it helps |
| - | - |
| "Our dashboard is showing stale data" | See if Coalesce refresh job failed, when it last ran successfully |
| "We're changing a source table schema" | Trace lineage to see which Coalesce nodes and downstream assets are affected |
| "Transformation is slow" | See query execution times, identify bottlenecks |
| "Data quality issue in production" | Check which Coalesce tests passed/failed, see test history |
| "Who changed this transformation?" | See deploy runs with commit info and user who triggered |
| "What does this Coalesce node actually do?" | View the SQL definition and column-level lineage |
# FAQ
### What access do you require?
We only request read access to your metadata and won't be able to read raw data or update or run any models.
### Will you store personal or business-critical data?
The platform only stores log-level data and doesn't access any actual data from your data warehouse.
### What if projects don't load?
If projects don't appear after entering your credentials:
1. Verify your API token is correct
2. Ensure the API endpoint matches your Coalesce region
3. Check that your token has the required read permissions
4. Try regenerating the token in Coalesce with appropriate permissions
### Can I use a custom Coalesce deployment?
Yes, select **Custom endpoint** from the region dropdown and enter your Coalesce API URL. This supports private deployments and custom domains.
### What if I need to change the region?
To change the region after creating the integration, you'll need to delete the integration and create a new one with the correct region.
# Azure DevOps Integration
Source: https://docs.synq.io/code-integrations/azure-devops
Add SSH keys to Azure DevOps Repos for Coalesce Quality
## Overview
This guide covers how to add the generated public SSH key to Azure DevOps Repos for repository access.
**Before you start:** Create your Git integration first by following the [integration form instructions](/code-integrations/overview#configuring-the-integration). You'll need the public SSH key from that integration.
## Prerequisites
* Access to Azure DevOps
* **Read permissions** on the repository you want to integrate
* Public SSH key from your Git integration
* A service account or user account to add the SSH key to
⏱️ **Estimated time:** 10 minutes
**Important:** Azure DevOps does not support repository-level deploy keys. You must use a service account or user account with SSH keys. The account should have **Read** permissions only on the repository.
## IP Whitelisting (If Required)
If your Azure DevOps organization uses IP restrictions, add the Coalesce Quality egress IP addresses for your region. See [IP Whitelist](/security/ip) for the complete list.
For instructions on configuring IP restrictions in Azure DevOps, see Azure DevOps' guide on [IP address restrictions](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-application-access-policies).
## Setting Up SSH Access
### Step 1: Create or Use a Service Account
1. Create a dedicated service account (recommended) or use an existing account
2. Grant the account **Read** permissions on the repository:
* Navigate to **Project Settings > Repositories**
* Select your repository
* Go to **Security** tab
* Add the service account with **Read** permission only
Using a dedicated service account makes it easier to audit and revoke access if needed.
### Step 2: Add SSH Public Key
1. Sign in to Azure DevOps with the service account
2. Click on your **User settings** icon (top right)
3. Select **SSH public keys**
4. Click **+ New Key**
5. Configure the SSH key:
* **Name:** Enter a descriptive name (e.g., `Coalesce Quality Integration`)
* **Public Key Data:** Paste the complete public SSH key from your integration
Make sure to paste the **complete key** including the `ssh-rsa` or `ssh-ed25519` prefix.
6. Click **Add**
The SSH key is now active for this account.
### Step 3: Verify Repository Access
Ensure the service account has access to the specific repository:
1. Navigate to **Project Settings > Repositories**
2. Select your repository
3. Go to **Security** tab
4. Verify the service account is listed with **Read** permissions
## Repository URL Format
When configuring the integration, use the Azure DevOps SSH URL format:
**Format:** `git@ssh.dev.azure.com:v3/organization/project/repository`
**Example:** `git@ssh.dev.azure.com:v3/mycompany/DataPlatform/analytics-repo`
The URL format for Azure DevOps SSH is different from other Git providers. Make sure to use the exact format shown above with `ssh.dev.azure.com:v3/`. This is automatically converted to `dev.azure.com` for the web URL.
### Commit URL Format
For most Azure DevOps instances, the correct commit URL format is detected automatically. If auto-detection doesn't work for your setup, you can specify it manually in the integration form.
**Recommended format (works with all Azure DevOps instances):**
`{web_url}/commit/{hash}`
The `{web_url}` placeholder automatically resolves to your full repository web URL including organization, project, and repository components.
**Alternative explicit format:**
`https://dev.azure.com/{owner}/{name}/commit/{hash}`
Note: This format may not work correctly if the project and repository cannot be properly parsed from your SSH URL. Use the `{web_url}` format for better reliability.
### Finding Your Repository URL
To find your repository's SSH URL:
1. Go to your repository in Azure DevOps
2. Click **Clone** button (top right)
3. Select **SSH** tab
4. Copy the URL (it will be in the format above)
## Troubleshooting
### SSH key not working
* Verify the **complete public key** was copied (including the `ssh-rsa` or `ssh-ed25519` prefix)
* Ensure the SSH key was added to the **correct user/service account**
* Confirm the account has **Read** permissions on the repository
### Connection refused
* Check that your organization's **IP restrictions** (if configured) include the Coalesce Quality egress IPs
* Verify the repository **SSH URL** uses the correct format: `git@ssh.dev.azure.com:v3/org/project/repo`
* Ensure the URL has `v3` in the path (required for Azure DevOps Git)
### Permission denied
* Verify the service account has **explicit Read permissions** on the repository
* Check that the account is not disabled or expired
* Ensure the SSH key is added to the same account that has repository access
### SSH URL format issues
* Azure DevOps requires a specific SSH URL format: `git@ssh.dev.azure.com:v3/org/project/repo`
* Do not use HTTPS URLs - only SSH URLs are supported
* The `v3` in the path is required
* Organization, project, and repository names are case-sensitive
## Security Best Practices
1. **Use a dedicated service account** for the integration
2. **Grant Read permissions only** - write access is never needed
3. **Regularly audit access** - Review service account permissions periodically
4. **Enable IP restrictions** - Limit access to the Coalesce Quality egress IPs
5. **Monitor access logs** - Check for unexpected authentication attempts
## Additional Resources
* [Azure DevOps SSH Authentication](https://learn.microsoft.com/en-us/azure/devops/repos/git/use-ssh-keys-to-authenticate)
* [Azure DevOps IP Address Restrictions](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-application-access-policies)
* [Azure DevOps Git Repository Permissions](https://learn.microsoft.com/en-us/azure/devops/repos/git/set-git-repository-permissions)
* [Back to Integration Overview](/code-integrations/overview)
# Bitbucket Integration
Source: https://docs.synq.io/code-integrations/bitbucket
Add deploy keys to Bitbucket repositories for Coalesce Quality
## Overview
This guide covers how to add the generated public SSH key as an access key (deploy key) to your Bitbucket repository.
**Before you start:** Create your Git integration first by following the [integration form instructions](/code-integrations/overview#configuring-the-integration). You'll need the public SSH key from that integration.
## Prerequisites
* Access to Bitbucket Cloud or Bitbucket Server/Data Center
* **Admin access** to your repository (required to add access keys)
* Public SSH key from your Git integration
⏱️ **Estimated time:** 5 minutes
## IP Whitelisting (If Required)
If your Bitbucket workspace uses IP allowlisting, add the Coalesce Quality egress IP addresses for your region. See [IP Whitelist](/security/ip) for the complete list.
For instructions on configuring IP allowlisting in Bitbucket, see Bitbucket's guide on [IP allowlisting](https://support.atlassian.com/bitbucket-cloud/docs/control-access-to-your-private-content/).
## Adding the Access Key
Bitbucket CloudBitbucket Server/Data Center
### Bitbucket Cloud
Follow these steps to add an access key (deploy key) to your Bitbucket Cloud repository.
#### Navigate to Repository Settings
1. Go to your repository in Bitbucket Cloud
2. Click **Repository settings** (gear icon in the left sidebar)
#### Add Access Key
3. Under **Security**, click **Access keys**
4. Click **Add key**
5. Configure the access key:
* **Label:** Enter a descriptive name (e.g., `Coalesce Quality Integration`)
* **Key:** Paste the public SSH key from your integration
**Do not enable write access.** Only read-only access is required.
6. Click **Add SSH key**
That's it! Your Bitbucket repository is now connected in read-only mode.
#### Documentation
For more details, see Bitbucket's official guide on [Access Keys](https://support.atlassian.com/bitbucket-cloud/docs/add-access-keys/).
### Bitbucket Server/Data Center
Follow these steps to add an access key to your Bitbucket Server or Data Center repository.
#### Navigate to Repository Settings
1. Navigate to your repository in Bitbucket Server
2. Click **Repository settings** in the left sidebar
#### Add Access Key
3. Under **Security**, click **Access keys**
4. Click **Add key**
5. Configure the access key:
* **Key:** Paste the public SSH key from your integration
* **Permission:** Set to **Read** only
**Set permissions to Read only.** Write access is not required.
6. Click **Add key**
The access key is now active. Your Bitbucket repository is connected in read-only mode.
#### Documentation
For more details, see Bitbucket's documentation on [SSH Access Keys](https://confluence.atlassian.com/bitbucketserver/ssh-access-keys-for-system-use-776639781.html).
## Repository URL Format
When configuring the integration, use the SSH URL format for Bitbucket:
**Format:** `git@bitbucket.org:workspace/repository.git`
**Example:** `git@bitbucket.org:mycompany/analytics-platform.git`
For Bitbucket Server/Data Center, the format may vary:
* `git@bitbucket.example.com:project/repository.git`
* `ssh://git@bitbucket.example.com:7999/project/repository.git`
## Troubleshooting
### Access key not working
* Verify the **complete public key** was copied (including the `ssh-rsa` or `ssh-ed25519` prefix)
* Ensure the access key was added to the **correct repository**
* Confirm the key has **read-only permissions**
### Connection refused
* Check that your workspace's **IP allowlist** (if configured) includes the Coalesce Quality egress IPs
* Verify the repository **SSH URL** is correct in the integration form
* For **Bitbucket Server**, ensure the instance is accessible from the Coalesce Quality IP addresses
### No Repository Settings visible
* Confirm you have **admin access** to the repository
* Check with your Bitbucket administrator for access permissions
### SSH URL format issues
* Bitbucket Cloud uses: `git@bitbucket.org:workspace/repo.git`
* Bitbucket Server may use a custom port: `ssh://git@hostname:7999/project/repo.git`
* Do not use HTTPS URLs - only SSH URLs are supported
## Additional Resources
* [Bitbucket Cloud Access Keys Documentation](https://support.atlassian.com/bitbucket-cloud/docs/add-access-keys/)
* [Bitbucket Server SSH Access Keys](https://confluence.atlassian.com/bitbucketserver/ssh-access-keys-for-system-use-776639781.html)
* [Bitbucket IP Allowlisting](https://support.atlassian.com/bitbucket-cloud/docs/control-access-to-your-private-content/)
* [Back to Integration Overview](/code-integrations/overview)
# GitHub Integration
Source: https://docs.synq.io/code-integrations/github
Add deploy keys to GitHub repositories for Coalesce Quality
## Overview
This guide covers how to add the generated public SSH key as a deploy key to your GitHub repository.
**Before you start:** Create your Git integration first by following the [integration form instructions](/code-integrations/overview#configuring-the-integration). You'll need the public SSH key from that integration.
## Prerequisites
* Access to GitHub
* **Admin access** to your repository (required to add deploy keys)
* Public SSH key from your Git integration
⏱️ **Estimated time:** 5 minutes
## IP Whitelisting (If Required)
If your GitHub organization uses IP allow lists, add the Coalesce Quality egress IP addresses for your region. See [IP Whitelist](/security/ip) for the complete list.
For instructions on adding IP addresses to GitHub, see GitHub's guide on [managing allowed IP addresses](https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-allowed-ip-addresses-for-your-organization).
## Adding the Deploy Key
Follow these steps to add the public key as a deploy key to your GitHub repository.
### Navigate to Your Repository
1. In the upper-right corner of any GitHub page, click your **profile photo**, then click **Your profile**
2. On your profile page, click **Repositories**, then click the name of your repository
### Access Repository Settings
3. From your repository, click **Settings**
If you don't see the **Settings** tab, you likely don't have admin access to the repository. Contact your repository administrator to add the deploy key.
### Add the Deploy Key
4. In the sidebar, click **Deploy Keys**, then click **Add deploy key**
5. Configure the deploy key:
* **Title:** Enter a descriptive name (e.g., `Coalesce Quality Integration`)
* **Key:** Paste the public key from your integration (Step 1)
* **Allow write access:** Leave this checkbox **unticked** (unchecked)
**Do not grant write access.** Coalesce Quality only requires read-only access to your repository.
6. Click **Add key** to save the deploy key
That's it! Your GitHub repository is now connected in read-only mode.
## Troubleshooting
### Deploy key not working
* Verify the **complete public key** was copied (including the `ssh-rsa` or `ssh-ed25519` prefix)
* Ensure the deploy key was added to the **correct repository**
* Confirm **write access is disabled** (unchecked)
### Connection refused
* Check that your GitHub organization's **IP allow list** (if configured) includes the Coalesce Quality egress IPs
* Verify the repository **SSH URL** is correct in the integration form
### No Settings tab visible
* Confirm you have **admin access** to the repository
* Organization owners may need to adjust access permissions
## Additional Resources
* [GitHub Deploy Keys Documentation](https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys)
* [GitHub IP Allow Lists](https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-allowed-ip-addresses-for-your-organization)
* [Back to Integration Overview](/code-integrations/overview)
# GitLab Integration
Source: https://docs.synq.io/code-integrations/gitlab
Add deploy keys to GitLab projects for Coalesce Quality
## Overview
This guide covers how to add the generated public SSH key as a deploy key to your GitLab project.
**Before you start:** Create your Git integration first by following the [integration form instructions](/code-integrations/overview#configuring-the-integration). You'll need the public SSH key from that integration.
## Prerequisites
* Access to GitLab (GitLab.com or self-managed instance)
* **Maintainer or Owner access** to your project (required to add deploy keys)
* Public SSH key from your Git integration
⏱️ **Estimated time:** 5 minutes
## IP Whitelisting (If Required)
If your GitLab group uses IP address restrictions, add the Coalesce Quality egress IP addresses for your region. See [IP Whitelist](/security/ip) for the complete list.
For instructions on configuring IP restrictions in GitLab, see GitLab's guide on [IP address restrictions](https://docs.gitlab.com/ee/user/group/access_and_permissions.html#restrict-group-access-by-ip-address).
## Adding the Deploy Key
Follow these steps to add the public key as a deploy key to your GitLab project.
### Navigate to Repository Settings
1. On the top bar, select **Menu > Projects** and find your project
2. On the left sidebar, select **Settings > Repository**
If you don't see the **Settings** menu, you likely don't have Maintainer or Owner access to the project. Contact your project administrator to add the deploy key.
### Add the Deploy Key
3. Expand **Deploy keys**
4. Configure the deploy key:
* **Title:** Enter a descriptive name (e.g., `Coalesce Quality Integration`)
* **Key:** Paste the public key from your integration (Step 1)
* **Grant write permissions to this key:** Leave this checkbox **unticked** (unchecked)
**Do not grant write permissions.** Coalesce Quality only requires read-only access to your repository.
5. Click **Add key** to save the deploy key
The deploy key is enabled immediately upon creation. Your GitLab project is now connected in read-only mode.
## Troubleshooting
### Deploy key not working
* Verify the **complete public key** was copied (including the `ssh-rsa` or `ssh-ed25519` prefix)
* Ensure the deploy key was added to the **correct project**
* Confirm **write permissions are disabled** (unchecked)
### Connection refused
* Check that your GitLab group's **IP address restrictions** (if configured) include the Coalesce Quality egress IPs
* Verify the repository **SSH URL** is correct in the integration form
* For **self-managed GitLab**, ensure the instance is accessible from the Coalesce Quality IP addresses
### No Settings menu visible
* Confirm you have **Maintainer or Owner access** to the project
* Check with your GitLab administrator for access permissions
## Additional Resources
* [GitLab Deploy Keys Documentation](https://docs.gitlab.com/ee/user/project/deploy_keys/)
* [GitLab IP Address Restrictions](https://docs.gitlab.com/ee/user/group/access_and_permissions.html#restrict-group-access-by-ip-address)
* [Back to Integration Overview](/code-integrations/overview)
# Code Integration Requirements
Source: https://docs.synq.io/code-integrations/overview
Requirements and supported Git providers for code integrations
## Overview
Coalesce Quality integrates with your Git repositories to extract information about code changes across your data platform. The integration works with multiple frameworks and tools, including:
* **dbt** (Data Build Tool)
* **Looker** (LookML)
* **SQLMesh**
* **Coalesce** (Transform)
* **Custom entities**
* **Any other data transformation or analytics code**
By connecting your Git repositories, you can:
* **Display model changes** in the UI with optional diffs for quick inspection and troubleshooting
* **Show recent Git commits** for failing models in alerts, helping you identify what changed
* **Link code changes to data assets** for easier debugging
* **Understand the impact** of code changes on data quality
* **Track lineage** from code to data warehouse
* **Provide context** for data incidents with relevant code changes
## Supported Git Providers
Coalesce Quality works with **any Git provider** that supports SSH access. We provide native support and detailed guides for the following:
### Primary Supported Providers
* **GitHub** (Cloud and Enterprise)
* **GitLab** (Cloud and self-managed)
* **Bitbucket** (Cloud and Server/Data Center)
* **Azure DevOps Repos**
### Other Providers
* **Self-hosted Git** (Gitea, Gogs, etc.)
* **Any other SSH-based Git provider**
The unified integration form works with all SSH-based Git providers. Choose your provider below for specific deploy key setup instructions:
* [GitHub Integration](/code-integrations/github) - Deploy key setup for GitHub
* [GitLab Integration](/code-integrations/gitlab) - Deploy key setup for GitLab
* [Bitbucket Integration](/code-integrations/bitbucket) - Access key setup for Bitbucket
* [Azure DevOps Integration](/code-integrations/azure-devops) - SSH key setup for Azure DevOps
* [Other Git Providers](/code-integrations/ssh-git) - Self-hosted and other SSH-based Git
## Integration Requirements
Your Git provider must meet the following requirements:
### 1. Internet Accessibility
Your Git repository must be accessible over the internet. Access can be limited to static egress IP addresses for your region. See [IP Whitelist](/security/ip) for the complete list.
### 2. SSH Access
* Git repository must be accessible via **SSH** (typically port 22)
* Only SSH URLs are supported - HTTPS URLs will not work
* SSH deploy keys are used for secure, read-only access
### 3. Authentication
* Repositories must support authentication using **deploy keys** (SSH keys)
* Following the principle of least privilege, only **read-only access** to the selected repository is required
## IP Whitelisting
If your organization uses IP whitelisting for Git provider access, you'll need to add the Coalesce Quality egress IP addresses to your allowlist. Consult your provider's documentation for specific instructions:
### GitHub
* [Adding an Allowed IP Address to GitHub](https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-allowed-ip-addresses-for-your-organization)
### GitLab
* [IP Address Restrictions in GitLab](https://docs.gitlab.com/ee/user/group/access_and_permissions.html#restrict-group-access-by-ip-address)
## Setup Overview
Setting up a code integration involves two main steps:
1. **Configure the integration** using the universal Git integration form
2. **Add the deploy key to your Git provider** following provider-specific instructions
## Configuring the Integration
All Git providers use the same integration form. Navigate to **Settings > Add integration > Git** to get started.
### Integration Name
Provide a descriptive name for your integration.
**Examples:**
* `Production Analytics Repo`
* `Data Platform GitHub`
* `Bitbucket Staging`
### Repository URL (SSH)
Enter the SSH URL of your Git repository.
**Format:** `git@hostname:organization/repository.git`
**Examples:**
* GitHub: `git@github.com:myorg/analytics.git`
* GitLab: `git@gitlab.com:myorg/data-platform.git`
* Bitbucket: `git@bitbucket.org:myworkspace/dbt-project.git`
Only SSH URLs are supported. HTTPS URLs will not work.
### Branches to Analyse
Specify which branches to analyze.
* **Default:** Leave empty to automatically use `main` or `master`
* **Custom:** Enter specific branch names (e.g., `develop`, `staging`, `production`)
### Path to Analyse
Define the directory path where your source code is located.
* **Default:** Leave empty to analyze the entire repository
* **Custom:** Enter a subdirectory path (e.g., `analytics/`, `transform/dbt/`)
In most cases, analyzing the entire repository (default) is recommended.
### Commit URL Format (Optional)
For most Git providers (GitHub, GitLab, Bitbucket), the correct commit URL format is determined automatically. For custom or self-hosted instances, you may need to specify it manually.
**Available Placeholders:**
* `{owner}` - Repository owner/organization
* `{name}` - Repository name
* `{hash}` - Commit hash
* `{branch}` - Branch name
* `{web_url}` - Repository web URL
* `{provider}` - Git provider (github, gitlab, bitbucket, azuredevops)
**Format Examples:**
* **GitHub:** `https://github.com/{owner}/{name}/commit/{hash}`
* **GitLab:** `https://gitlab.com/{owner}/{name}/-/commit/{hash}`
* **Bitbucket:** `https://bitbucket.org/{owner}/{name}/commits/{hash}`
* **Custom:** `{web_url}/commit/{hash}` (works with most Git providers)
Leave this field empty unless you're using a custom Git instance that requires a specific URL format.
### Data Collection
**Store commit diff:** Enable this option (checked by default) to collect and display actual code changes in the Code & Changes screen.
### Create and Get Public Key
Click **Create** to generate the integration. After creation, navigate back to edit the integration to view the **public SSH key**.
The public key is generated when you create the integration but is only visible when you edit the integration afterward. Make sure to copy it before proceeding to add it to your Git provider.
## Access Permissions
**Admin access** to the repository is required to configure deploy keys. However, the deploy key itself only provides **read-only access** to the repository content. This ensures:
* Your code cannot be modified
* Access is limited to the specific repository
* No organization-wide permissions are granted
## Adding Deploy Keys to Your Git Provider
After creating the integration and copying the public SSH key, you need to add it as a deploy key in your Git provider. The process varies by provider.
Choose your Git provider for detailed setup instructions:
Add deploy key to GitHub repositories
Add deploy key to GitLab projects
Add access key to Bitbucket repositories
Add SSH key to Azure DevOps Repos
Self-hosted and other SSH-based Git
# Other Git Providers
Source: https://docs.synq.io/code-integrations/ssh-git
Add deploy keys to self-hosted and other SSH-based Git providers
## Overview
This guide covers how to add the generated public SSH key to self-hosted and other SSH-based Git providers, including:
* **Self-hosted Git** (Gitea, Gogs, GitBucket, etc.)
* **Any other SSH-based Git provider**
For GitHub, GitLab, Bitbucket, and Azure DevOps, see their dedicated setup guides:
* [GitHub Integration](/code-integrations/github)
* [GitLab Integration](/code-integrations/gitlab)
* [Bitbucket Integration](/code-integrations/bitbucket)
* [Azure DevOps Integration](/code-integrations/azure-devops)
**Before you start:** Create your Git integration first by following the [integration form instructions](/code-integrations/overview#configuring-the-integration). You'll need the public SSH key from that integration.
## Prerequisites
* Admin or appropriate access to configure SSH keys/deploy keys
* Public SSH key from your Git integration
* Git provider accessible over the internet via SSH
⏱️ **Estimated time:** 10-15 minutes
## Adding SSH Keys to Self-Hosted Git
The process for adding SSH keys to self-hosted Git instances varies by software, but generally follows this pattern:
### General Setup Steps
1. Navigate to your **repository settings** in your Git interface
2. Look for one of these sections:
* **Deploy Keys** (most common)
* **Access Keys**
* **SSH Keys**
3. Click **Add key** or similar button
4. Configure the key:
* **Title/Name:** Enter a descriptive name (e.g., `Coalesce Quality Integration`)
* **Key:** Paste the complete public SSH key from your integration
* **Permissions:** Select **Read-only** or equivalent
5. Save the key
Always configure the key with **read-only permissions**. Coalesce Quality never needs write access to your repository.
### Common Self-Hosted Git Platforms
1. Go to your repository in Gitea
2. Click **Settings** > **Deploy Keys**
3. Add the title and paste the public key
4. **Do not** check "Grant Write Access"
5. Click **Add Deploy Key**
For more details, see the [Gitea Documentation](https://docs.gitea.com/).
1. Navigate to your repository
2. Go to **Settings** > **Deploy Keys**
3. Enter a key name and paste the public key
4. Click **Add Deploy Key**
[Gogs Deploy Keys Documentation](https://gogs.io/)
1. Open your repository settings
2. Navigate to **Deploy Keys**
3. Add the title and public key
4. Set permission to **Read Only**
5. Click **Add**
[GitBucket Documentation](https://github.com/gitbucket/gitbucket)
For other self-hosted Git software:
1. Consult your Git software's documentation for SSH key or deploy key configuration
2. Look for repository-level SSH key settings (preferred over user-level keys)
3. Ensure the key is configured with read-only access
4. Test the connection after adding the key
Most Git software follows similar patterns to the examples above.
### Requirements for Self-Hosted Git
Ensure your self-hosted Git instance meets these requirements:
* **Internet accessible:** Must be reachable from the Coalesce Quality IP addresses
* **SSH enabled:** SSH protocol must be enabled (typically port 22)
* **Firewall configured:** Allow connections from the Coalesce Quality egress IPs
* **Valid SSL certificate:** If using HTTPS for the web interface
## IP Whitelisting (If Required)
If your Git provider uses IP whitelisting, add the Coalesce Quality egress IP addresses for your region. For self-hosted Git instances, configure your firewall to allow connections from these IP addresses.
See [IP Whitelist](/security/ip) for the complete list.
## Troubleshooting
### Deploy key/SSH key not working
* Verify the **complete public key** was copied (including the `ssh-rsa` or `ssh-ed25519` prefix)
* Ensure the key was added to the **correct repository or account**
* Confirm the key has **read-only permissions**
### Connection refused
* Check that **IP whitelisting** (if configured) includes the Coalesce Quality egress IPs
* Verify the repository **SSH URL** is correct in the integration form
* For **self-hosted Git**, ensure the instance is accessible from the Coalesce Quality IP addresses
### Self-hosted Git issues
* Confirm your Git instance is accessible over the **internet**
* Verify **firewall rules** allow connections from the Coalesce Quality IPs
* Check that **SSH is enabled** and listening on the expected port (usually 22)
* Test SSH connectivity: `ssh -T git@your-hostname.com`
## Additional Resources
* [IP Whitelist](/security/ip)
* [Back to Integration Overview](/code-integrations/overview)
* [GitHub Integration Guide](/code-integrations/github)
* [GitLab Integration Guide](/code-integrations/gitlab)
## Need Help?
If you're having trouble connecting your Git provider, [contact support](/support/support) with:
* Your Git provider type (Bitbucket, Azure DevOps, self-hosted, etc.)
* The SSH URL format you're using
* Any error messages from the connection attempt
# Agent Workflow
Source: https://docs.synq.io/custom-integrations/agent-workflow
A self-contained brief for a coding agent writing a custom integration against the Coalesce Quality API
This page is written to be handed to a coding agent. Point it here and describe your tool:
> Read [https://docs.synq.io/custom-integrations/agent-workflow](https://docs.synq.io/custom-integrations/agent-workflow) and follow it.
> Write an integration for ``. Its API is at ``.
Everything an agent needs is below. The rest of this section is the same material for a human
reader.
***
## Your task
Write a program that reads a tool Coalesce Quality has no integration for and publishes it to
the Coalesce Quality catalog over the public API: entities, schemas, lineage, checks and run
status.
The program must be **idempotent**. It runs on a schedule, not once. Running it twice
produces the same estate.
## Ground rules
Read all of these before writing code. Each one exists because getting it wrong produces a
valid-looking write and a broken catalog, with no error anywhere.
1. **The entity id is a lowercase slug built from the source system's stable id.** Restrict it
to `A-Z a-z 0-9 : _ . -`, shaped `::::`. Every human-readable string
— the title, accents, spaces — goes in `name` and `description` instead. See
[Entity ids](#entity-ids).
2. **Declare schemas before writing any lineage.** A `SELECT *` expands only over an entity
whose columns are known. Skip this and table-level edges still appear while column-level
ones silently do not. Note that a *declared* `ColumnLineage` edge is **not** validated
against the upstream schema — a typo in `upstream_column` is accepted and yields a broken
hop, so verification in step 9 is what catches it.
3. **Bind every name in SQL that is not a warehouse object.** A `SqlDefinition` resolves names
that live at a warehouse address. Anything else resolves to nothing and is dropped with no
error. See [Binding](#binding-non-warehouse-names).
4. **Never send `category` or `governance_category` on a check.** They are resolved by the
workspace's own categorisation rules, and a producer value outranks those rules. Send
`package` and `kind` only.
5. **`feature_id` is a stable constant, never generated.** A fresh id each run creates a new
feature instead of replacing the previous one.
6. **The entity group is sent whole, every run.** A partial send is a deletion of everything
omitted. If reading the source tool can fail halfway, skip the group write that run.
7. **Read the lineage back at the end and fail loudly.** Assert the edges you expect, not
merely that some edge exists, and exit non-zero on a timeout or a non-OK CLL state. This is
the only thing that distinguishes a working integration from a broken one. Keep it in
production.
8. **Set the gRPC authority when building the channel**, or every RPC returns `UNIMPLEMENTED`.
Step 0 below has the option for each language.
## Step 0: setup
Everything you need is on this page. Follow it straight through rather than opening the other
pages; they say the same things at more length, and the two links that matter are named at the
points where you need them.
Credentials come from API settings, at `/settings/api` in the customer's own app host
(`app.synq.io` EU, `app.us.synq.io` US, `app.au.synq.io` AU), with these scopes:
**Edit Entity Types**, **Edit Entities**, **Edit Lineage**, **Edit Executions**, **Read
Entities**, **Read Entity Types**, **Read Lineage**.
Read them from the environment. Never hardcode them.
```
QUALITY_CLIENT_ID
QUALITY_CLIENT_SECRET
QUALITY_API_ENDPOINT developer.synq.io (EU) | api.us.synq.io (US) | api.au.synq.io (AU)
```
Exchange for a token at `POST https://$QUALITY_API_ENDPOINT/oauth2/token` with
`grant_type=client_credentials`, and send `authorization: Bearer ` on every call.
Refresh when `expires_in` elapses.
Use the prebuilt SDKs from [buf.build/getsynq/api](https://buf.build/getsynq/api/sdks) rather
than generating clients.
**Set the gRPC authority explicitly when you build the channel.** Without it the port travels
in the `Host` header and **every RPC returns `UNIMPLEMENTED` with no message** — which reads as
a missing service, not a routing problem, and is the single most likely reason a first run
fails entirely:
* Go: `grpc.WithAuthority(endpoint)` as a dial option.
* Python: `options=(("grpc.default_authority", endpoint),)` on `grpc.secure_channel`.
If an endpoint above does not resolve, the maintained list is the
[region table](https://docs.synq.io/security/ip); token exchange and client generation are
covered in full by [Getting Started](https://docs.synq.io/api-reference/getting-started).
Neither is needed to follow this page.
**Before writing code, read one of the complete examples**:
[Go](https://github.com/getsynq/api/tree/main/examples/golang/bi_tool_lineage),
[Python](https://github.com/getsynq/api/tree/main/examples/python/bi_tool_lineage). They are
verified against a live workspace and cover every step here.
## Step 1: map the source tool
Enumerate the kinds of object the tool has, and for each one decide:
* **Does it transform data?** → trait `is_model`
* **Is it a raw external input?** → trait `is_source`
* **Is it read by a person: a dashboard, a report?** → trait `is_bi_like`
* **Does it validate another object?** → trait `is_test_type`
* **Is it infrastructure nobody wants drawn?** → trait `is_ignored_in_lineage`
Traits combine. A saved query that transforms data *and* is read by a person is both
`is_model` and `is_bi_like`.
Then decide, per kind of edge between objects, which lineage mechanism applies:
| The downstream object has | Use |
| - | - |
| SQL, reading real warehouse tables | `SqlDefinition` |
| SQL, naming something with no warehouse address | `SqlDefinition` + `references` |
| No SQL, but a known column mapping | `ColumnLineage` |
| No SQL and no columns | `Relationship` |
| A validation role over another object | `CheckCategory` + check relationship |
Prefer `SqlDefinition` wherever there is SQL. A declaration is a snapshot; a parse follows the
query.
Write this mapping down in the source file as a comment before implementing it. It is the
design, and it is what a reviewer needs.
## Entity ids
A custom entity is named by `CustomIdentifier { id: "" }`.
**The id is a slug, not a label.** It is a machine identifier. Everything a person reads goes
in `name` (up to 500 characters, any UTF-8) and `description` (Markdown) — separate fields
with no restriction. Never put a title, a path with spaces, or accented text in the id; put it
in `name`.
Build every id to these three rules:
1. **From the source system's stable id**, never a display name. A title changes; a primary
key does not. Changing an id creates a second entity rather than renaming the first.
2. **From the alphabet `A-Z a-z 0-9 : _ . -` only.** That is what the platform preserves
verbatim. Use `::` as the separator and prefix with the tool and object kind:
`::::`.
3. **Lowercased.** Ids are case-sensitive, so `Metabase::Question::87` and
`metabase::question::87` are two entities and nothing warns you. Lowercase at the single
place you build ids.
```
metabase::question::87 good
metabase::Revenue by category bad — a title, a space, a capital
```
Ignore this and the write normalises silently: truncated at 200 bytes (hash tail if longer),
non-ASCII replaced by its literal escape text (`zamówienia` becomes `zam_u00f3wienia`), and
every other character folded to `_`. **Two ids differing only in folded characters become the
same entity** — `metabase/question/87` and `metabase question 87` both become
`metabase_question_87`, second write overwriting the first.
The stored id is `custom-`, which is what the API returns as `entity_id`.
### Naming entities you did not create
Lineage points at warehouse tables, dbt models and dashboards that already exist.
**Use the structured variant for the platform** — `bigquery_table`, `snowflake_table`,
`databricks_table`, `postgres_table`, `mysql_table`, `clickhouse_table`, `redshift_table`,
`trino_table`, `mssql_table`, `oracle_table`, `athena_table`, `fabric_table`,
`dbt_core_node`, `dbt_cloud_node`, `sql_mesh_model`, `sql_mesh_audit`, `airflow_dag`,
`airflow_task`, `monitor`, `dataproduct`, `saved_view`. Construct these from the coordinates
the source tool reports.
`synq_path` is the fallback, for a platform with no structured variant (Looker, Tableau,
Coalesce Catalog and anything newer) or when you already hold an entity's opaque `entity_id`.
To obtain one:
* The app URL `/catalog//overview` — the segment after `/catalog/` is
the id. The app host is regional, matching the API endpoint above.
* The [MCP server](https://docs.synq.io/scout/mcp): `search_entities` and the other read tools.
* A [webhook payload](https://docs.synq.io/api-reference/webhook): each entity carries its
`identifier` and an `entity_url` containing the id.
* `BatchIdsByCoordinates`, which maps a warehouse name to matching ids.
`entity_id` is output only — on a request it is ignored, and the structured variant is what
resolves. To name an entity *by* opaque id, set `synq_path` to that string.
### Resolve before you write
`synq.entities.resolve.v1.IdentifierResolveService.BatchResolveIdentifiers` turns an
identifier of any shape into its `entity_id`, and with `check_existence: true` says whether
the entity exists.
The two kinds of upstream differ, and this is what tells them apart:
* A **custom** entity that does not exist is **rejected** at the write, so a misspelled custom
id surfaces immediately.
* An entity on a **connected platform** is **not checked**; it binds on next ingestion. So a
typo'd warehouse name and a not-yet-crawled table are indistinguishable in the lineage
afterwards.
Run `BatchResolveIdentifiers` over every connected-platform upstream and fail loudly on
anything that does not resolve.
It also returns the entity's **identity group** (`identities`, `identity_synq_paths`) — the
other identifiers that name the same entity, such as a dbt model and the warehouse table it
builds. Use it to detect that two upstreams you were treating as different are one entity
before writing two relationships that collapse into one edge.
### Use the read API as input
Do not hardcode a table of entity ids. Resolve them at run time:
| To answer | Call |
| - | - |
| Which entity is this warehouse name? | `BatchIdsByCoordinates` |
| Does this identifier resolve, and to what? | `BatchResolveIdentifiers` |
| Which entity does this name refer to? | `SearchEntities`, `ListEntities` |
| What columns does this upstream really have? | `GetSchema` |
| Which type numbers are already taken? | `ListTypes`, `ListEntityTypeDefs` |
| Where does this entity sit in the folder tree? | `Browse`, `GetFolderOf` |
Declaring column lineage against columns read from `GetSchema`, rather than columns the
source tool believes exist, is the difference between edges that appear and edges that do not.
## Step 2: types
`synq.entities.custom.v1.TypesService.UpsertType`, one per kind.
* `type_id` is `1`–`1000`, workspace-wide, with no allocator.
* **Call `ListTypes` first** and refuse to start if one of your numbers is taken under a
different name. Two integrations picking the same band is otherwise silent corruption.
* `name` is a short caption shown on a badge, max 100 chars: `"Kafka Topic"`, not a sentence.
* `svg_icon` is required, raw SVG bytes, max 1 MB.
* `traits` from step 1.
Keep the numbers in a constant table at the top of the file.
## Step 3: entities
`synq.entities.custom.v1.EntitiesService.UpsertEntity`, one per object.
**Create every entity before anything points at one.** A SQL binding to an entity that does
not exist is rejected at the write.
* `id` — `CustomIdentifier`, from the source system's stable id.
* `type_id` — from step 2.
* `name` — as the tool shows it, max 500 chars. Do not shorten it to fit.
* `description` — Markdown, max 10 000 chars.
* `annotations` — the tool's own filing system: collection, folder, workspace, object kind.
Max 20 annotations, each with a name (max 50 chars) and up to 20 values (max 100 chars).
These are what people filter the catalog by, so keep them to labels, not prose.
Never set `synq_path`, `synq_catalog_url`, `ids` or `synq_paths` — they are returned, not sent.
## Step 4: schemas
`FeaturesService.UpsertEntityFeature` with a `schema` feature, `feature_id: "schema"`.
Do this **before** step 5. Columns carry a name, native type, description and ordinal
position. Give positions for all columns or for none. Max 1000 columns; struct and repeated
types nest through `fields`.
## Step 5: lineage
### SqlDefinition
`feature_id: "sql"`. One per entity.
* `dialect` — one of `SQL_DIALECT_{BIGQUERY,CLICKHOUSE,DATABRICKS,MYSQL,POSTGRESQL,REDSHIFT,SNOWFLAKE,DUCKDB,TRINO,MSSQL,ORACLE,ATHENA,FABRIC}`.
* `sql` — as the tool actually executes it, max 1 000 000 chars.
* `database_context` — what unqualified names in the SQL resolve against.
* `references` — see below. Max 200.
### Binding non-warehouse names
A `SqlDefinition` can only resolve names that live at a warehouse address. A saved query, a
queue, a service, an API or another custom entity has no address, so `FROM my_upstream`
matches nothing, is dropped, and the entity ends up with **no upstream at all and no error**.
An empty result is indistinguishable from a query that genuinely reads nothing. This is the
single most common way a custom integration ships broken.
`references` binds a name, as the SQL writes it, to the entity it stands for:
```
SqlTableReference {
instance_name "" empty parts default from database_context
database_name ""
schema_name ""
object_name "order_items_enriched" the name exactly as the SQL writes it
entity
}
```
* Matching is case-insensitive.
* A binding **beats** a real warehouse table of the same name — use it as a deliberate
override when you need one.
* Empty parts default from `database_context`, exactly as an unqualified name in the SQL does.
* Names that **are** warehouse objects need no binding. One definition can read a bound custom
entity and a raw table in the same query, binding only the first.
* Bindings replace with the definition. One dropped from a later write is gone.
### ColumnLineage
`feature_id: "column-lineage"`. One per entity, max 10 000 edges.
Use when there is no SQL for a parser to read. Each `ColumnEdge` is `upstream` (an
`Identifier`), `upstream_column` and `column`. The two sides are named independently and need
not match.
* **It replaces whole.** An edge missing from a write is withdrawn. Regenerate it from the
tool's metadata every run rather than diffing.
* **A column edge declares the table edge it implies.** Do not also write a `Relationship`.
* **It wins over `SqlDefinition`.** An entity with both is served from this declaration. So
either declare only what the SQL cannot say, or make this the authority for the entity's
whole column lineage.
* An upstream that does not exist yet is remembered, and the edge appears once it does.
* `state_at` defaults to now; set it explicitly when backfilling.
### Relationship
`RelationshipsService.UpsertRelationships`, for edges with nothing to say at column grain.
At least one endpoint must be a custom entity — two native entities cannot be joined.
**Read the response.** Each `RelationshipWriteResult` names the resolved endpoints and an
outcome of `CREATED`, `UPDATED`, `DELETED` or `NOT_FOUND`. An edge is held between the
entities the endpoints *resolve* to, and one entity accepts several spellings, so two
relationships that look different can be the same edge — and only the last write survives.
### Code and Git
Optional but worth including:
* `code` feature (`feature_id` per file or function, several allowed): the SQL, Python, YAML
or JSON behind the entity, max 100 000 chars, with a `code_type` of
`CODE_TYPE_{SQL,PYTHON,JSON,YAML,DBT,SQLMESH,LOOKML,JAVASCRIPT,BASH,DAX,JAVA,SCALA,TEXT,MARKDOWN}`.
* `git_file_reference` feature: repository URL (SSH preferred), branch, file path. With a code
integration connected, Coalesce Quality reads the file's history and shows recent commits
against the entity when it fails.
## Step 6: checks
For objects that validate other objects — an alert, a test, a monitor in the source tool.
1. `ChecksRelationshipsService.UpsertCheckRelationships` with `check`, `checked` and optional
`checked_columns`.
2. `check_category` feature, `feature_id: "check"`, carrying `package` (a grouping, max 50
chars) and `kind` (what the tool calls it, max 50 chars).
**That order matters.** Attaching a check relationship schedules no re-publish of the derived
check record, while writing a feature does. Writing the feature first leaves the check's stored
state stale until the entity is written again. If you ever delete a check relationship,
re-write the feature afterwards for the same reason.
**Do not set `category` or `governance_category`.** They are resolved by the workspace's own
[categorisation rules](https://docs.synq.io/analytics/check-categories) from `package` and
`kind`. A producer value outranks those rules, so deriving one from the tool's alert kind —
the obvious thing to do — silently switches off every rule the workspace wrote, with nothing
in the product explaining why.
A check is not a stage data flows through. Do not also give it a lineage edge.
## Step 7: the entity group
`GroupsService.UpsertEntitiesGroup` with one `group_id` for your integration and **every
entity id it owns**.
The server diffs your set against the previous one and deletes what is missing. That is what
makes deletion work without keeping state on your side.
```
run 1: group = A, B, C
run 2: group = B, C, D -> A is deleted
```
A partial send is a deletion of everything omitted. If the source read can fail partway, skip
the group write for that run. Log `deleted_ids` from the response.
**A group deletes entities only.** It does not remove features, relationships or check
relationships belonging to entities that still exist. A dashboard repointed at a different
model, a check that was removed, a `SqlDefinition` that is no longer relevant — none of those
are cleaned up by the group write, and they stay in the graph indefinitely.
So the run also has to reconcile what it wrote last time:
1. Read the current state: `ListEntityFeatures` (with `entity_id` unset for the whole
workspace), `ListRelationships`, `ListCheckRelationships`.
2. Diff against the state you are about to write.
3. Withdraw what is gone, **before** the group write: `DeleteEntityFeature`,
`DeleteRelationships`, `DeleteCheckRelationships`.
4. Then write the group, which removes the entities themselves.
Withdrawing edges before deleting entities is the order that leaves no dangling references.
## Step 8: executions
`EntityExecutionsService.UpsertExecution`, so entities have health rather than just a position
in a graph.
* `status` — `EXECUTION_STATUS_{OK,WARN,ERROR,CRITICAL}`.
* `created_at` — required, must be in the past, rejected before 2022-01-01.
* `started_at` / `finished_at` — for long runs.
* `extras.executed_sql` — the SQL that ran.
Report what the tool tells you and nothing more. A run that has not happened yet is simply not
reported. `UpsertLogEntry` attaches log output without changing status.
## Step 9: verify
Call `LineageService.GetLineage` with `lineage_direction: LINEAGE_DIRECTION_UPSTREAM` and a
`start_point.entities` naming an entity at the bottom of your graph. Lineage is computed
asynchronously, so poll with a deadline.
**A non-empty result is not success.** Assert the specific nodes and edges you expect. A
declared column edge with a typo'd `upstream_column` still produces one hop while the chain
beyond it is silently lost, so "at least one edge exists" passes on a graph that is broken.
Fail the program, with a non-zero exit status, on any of:
* an expected node or edge missing from the response,
* a node whose `cll_details.cll_state` is neither OK nor unspecified — `RESOLUTION_FAILED` is
usually a name you should have bound in step 5 and did not,
* the poll deadline expiring, or the read erroring.
A timeout that logs and returns normally is the failure mode to avoid: it reports success for a
graph that never materialised.
## Definition of done
Check each of these before reporting the work complete:
* [ ] Ids are lowercase slugs from the source system's stable ids, in `A-Z a-z 0-9 : _ . -`
* [ ] No title, accent, space or other human-readable text is in an id; it is in `name`
* [ ] `ListTypes` is called before claiming type numbers
* [ ] Every type declares its traits
* [ ] Every entity with columns declares its schema, before lineage is written
* [ ] Every non-warehouse name in a `SqlDefinition` has a `references` binding
* [ ] Every upstream is resolved with `BatchResolveIdentifiers` before lineage is written
* [ ] Checks send `package` and `kind` only
* [ ] All `feature_id` values are constants
* [ ] The group is sent whole, and skipped entirely on a partial source read
* [ ] Obsolete features, relationships and check relationships are explicitly deleted; the
group write alone does not remove them
* [ ] Credentials are read from the environment
* [ ] The program polls `GetLineage` at the end and exits non-zero when it is empty
* [ ] Running the program twice produces no change on the second run
## Reference
* [Modelling your tool](/custom-integrations/modelling) — the decisions in more depth
* [Building an integration](/custom-integrations/building-an-integration) — the same steps with Go and Python code
* [API surface](/custom-integrations/api-surface) — every method, scope and limit
* [gRPC API reference](/api-reference/api) — generated from the protos
* [API scopes](/api-reference/scopes) — every scope and the methods it unlocks
* [Protos](https://github.com/getsynq/api/tree/main/protos) and [SDKs](https://buf.build/getsynq/api/sdks)
* [Examples](https://github.com/getsynq/api/tree/main/examples) — runnable Go and Python
Questions that this page does not answer go to your Technical Account Manager — see
[Support](/support/support).
# API Surface
Source: https://docs.synq.io/custom-integrations/api-surface
Every service a custom integration calls, the scope each method needs, and the limits that apply
The methods a custom integration uses, grouped by what they do. Full generated reference:
[gRPC API](/api-reference/api), [API Scopes](/api-reference/scopes),
[OpenAPI spec](https://github.com/getsynq/api/blob/main/openapi.yaml).
All services are under `synq.entities.*`. REST paths are given for the transcoded HTTP API;
the same methods are available over gRPC.
## Write
### Types
`synq.entities.custom.v1.TypesService`
| Method | REST | Scope |
| - | - | - |
| `UpsertType` | `POST /api/entities/custom/v1/types` | Edit Entity Types |
| `DeleteType` | `DELETE /api/entities/custom/v1/types` | Edit Entity Types |
| `ListTypes` | `POST /api/entities/custom/v1/types/list` | Read Entity Types |
`Type` carries `type_id` (`1`–`1000`), `name` (max 100 chars), `svg_icon` (max 1 MB) and
`traits`.
### Entities
`synq.entities.custom.v1.EntitiesService`
| Method | REST | Scope |
| - | - | - |
| `UpsertEntity` | `POST /api/entities/custom/v1/entities` | Edit Entities |
| `DeleteEntity` | `DELETE /api/entities/custom/v1/entities` | Edit Entities |
| `ListEntities` | `GET /api/entities/custom/v1/entities` | Read Entities |
`Entity` carries `id`, `type_id`, `name` (max 500 chars), `description` (max 10 000 chars,
Markdown) and up to 20 `annotations`. Each annotation has a name (max 50 chars) and up to 20
values (max 100 chars each).
`synq_path`, `synq_catalog_url`, `ids` and `synq_paths` are returned, never sent.
### Features
`synq.entities.custom.v1.FeaturesService`
| Method | REST | Scope |
| - | - | - |
| `UpsertEntityFeature` | `POST /api/entities/custom/v1/features` | Edit Entities |
| `DeleteEntityFeature` | `DELETE /api/entities/custom/v1/features` | Edit Entities |
| `ListEntityFeatures` | `POST /api/entities/custom/v1/features/list` | Read Entities |
A `Feature` is `entity_id` + `feature_id` (max 255 chars, **stable**) + one of:
| Feature | Per entity | Limits |
| - | - | - |
| `sql_definition` | one | `sql` max 1 000 000 chars, `references` max 200 |
| `schema` | one | `columns` max 1000, nested `fields` max 100 per column |
| `column_lineage` | one | `edges` max 10 000, column names max 255 chars |
| `check_category` | one | `package` and `kind` max 50 chars each |
| `code` | many | `content` max 100 000 chars, `name` max 50 chars |
| `git_file_reference` | many | `repository_url` and `file_path` max 255 chars, `branch_name` required, max 244 bytes |
`ListEntityFeatures` with `entity_id` unset returns the features of every custom entity in
the workspace.
### Groups
`synq.entities.custom.v1.GroupsService`
| Method | REST | Scope |
| - | - | - |
| `UpsertEntitiesGroup` | `POST /api/entities/custom/v1/groups` | Edit Entities |
| `DeleteEntitiesGroup` | `DELETE /api/entities/custom/v1/groups` | Edit Entities |
| `ListEntityGroups` | `POST /api/entities/custom/v1/groups/list` | Read Entities |
`UpsertEntitiesGroup` replaces the group's membership whole and returns `deleted_ids` — the
entities that were in the group and are not in this write.
### Relationships
`synq.entities.custom.v1.RelationshipsService`
| Method | REST | Scope |
| - | - | - |
| `UpsertRelationships` | `POST /api/entities/custom/v1/relationships` | Edit Lineage |
| `DeleteRelationships` | `DELETE /api/entities/custom/v1/relationships` | Edit Lineage |
| `ListRelationships` | `POST /api/entities/custom/v1/relationships/list` | Read Lineage |
Both writes return one `RelationshipWriteResult` per requested relationship, in request
order, each with the resolved endpoints and an outcome of `CREATED`, `UPDATED`, `DELETED` or
`NOT_FOUND`.
At least one endpoint must be a custom entity.
### Check relationships
`synq.entities.custom.v1.ChecksRelationshipsService`
| Method | REST | Scope |
| - | - | - |
| `UpsertCheckRelationships` | `POST /api/entities/custom/v1/check-relationships` | Edit Entities |
| `DeleteCheckRelationships` | `DELETE /api/entities/custom/v1/check-relationships` | Edit Entities |
| `ListCheckRelationships` | `POST /api/entities/custom/v1/check-relationships/list` | Read Entities |
A `CheckRelationship` is `check` + `checked` + optional `checked_columns`. The pair is the
identity; restating it with a different column list replaces the stored one.
### Executions
`synq.entities.custom.v1.EntityExecutionsService`
| Method | REST | Scope |
| - | - | - |
| `UpsertExecution` | `POST /api/entities/custom/executions/v1` | Edit Executions |
| `UpsertLogEntry` | `POST /api/entities/custom/executions/v1/log-entry` | Edit Executions or Edit Execution Logs |
`Execution` requires `id`, `status` and `created_at`. Status is one of `EXECUTION_STATUS_OK`,
`_WARN`, `_ERROR`, `_CRITICAL`. `created_at` must be in the past and later than 2022-01-01.
`extras` can carry the executed SQL.
## Read
Reading back is how you verify a write landed. These are the methods worth calling.
| Method | Service | REST | Scope |
| - | - | - | - |
| `BatchResolveIdentifiers` | `synq.entities.resolve.v1.IdentifierResolveService` | `POST /api/resolve/v1/by-id` | Read Entities |
| `GetLineage` | `synq.entities.lineage.v1.LineageService` | `POST /api/lineage/v1` | Read Lineage |
| `GetImpact` | `synq.entities.impact.v1.ImpactService` | `POST /api/impact/v1` | Read Lineage |
| `GetSchema` | `synq.entities.schemas.v1.SchemasService` | `GET /api/schema/v1` | Read Entities |
| `SearchEntities` | `synq.entities.entities.v1.EntitiesService` | `GET /api/entities/v1/search` | Read Entities |
| `BatchGetEntities` | `synq.entities.entities.v1.EntitiesService` | `POST /api/entities/v1/by-id` | Read Entities |
| `BatchGetEntityAnnotations` | `synq.entities.annotations.v1.AnnotationsService` | `POST /api/entities/annotations/v1/batchGet` | Read Entities |
| `ListEntityTypeDefs` | `synq.entities.typedefs.v1.TypeDefsService` | `POST /api/entities/typedefs/v1/entity-types/list` | Read Entities or Read Entity Types |
| `GetLatestExecutions` | `synq.entities.executions.v2.EntityExecutionsService` | see [API reference](/api-reference/api) | Read Executions |
| `BatchIdsByCoordinates` | `synq.entities.coordinates.v1.DatabaseCoordinatesService` | `POST /api/coordinates/v1/database/by-fqn` | Read Entities |
`BatchIdsByCoordinates` is how you turn a warehouse name your tool reports into the entity
id Coalesce Quality holds for it — useful when your tool names tables as strings.
`BatchResolveIdentifiers` turns an identifier of **any** shape into its `entity_id`, and with
`check_existence: true` reports whether the entity exists. It also returns the entity's whole
**identity group** — the other identifiers naming the same entity, such as a dbt model and the
warehouse table it builds.
Run it over the upstreams your integration is about to name. A **custom** entity that does not
exist is rejected at the write, so you find that one either way; an entity on a **connected
platform** is not checked and binds on next ingestion, so a typo'd warehouse name and a
not-yet-crawled table look identical afterwards. Resolving separates them.
The read methods above are meant to be driven from your integration as input, not only used
to verify a write. Resolving coordinates, schemas and type numbers at run time is what keeps a
mapping from drifting against a hardcoded table of ids.
## Identifiers
A custom entity is named by `CustomIdentifier`:
```json theme={null}
{ "custom": { "id": "metabase::question::87" } }
```
The `id` is a **slug**, not a label: keep it to a lowercase `A-Z a-z 0-9 : _ . -` string built
from the source system's stable id, and put everything human-readable in `name` and
`description`. On write it is truncated at 200 bytes with a hash tail if longer, non-ASCII is
replaced by its literal escape text, and every other character is folded to `_` — so two ids
differing only in folded characters are **the same entity**, while ids differing only in case
are **two**. The stored id is `custom-`.
Entities from connected platforms are named by their platform's own variant —
`bigquery_table`, `snowflake_table`, `databricks_table`, `postgres_table`, `mysql_table`,
`clickhouse_table`, `redshift_table`, `trino_table`, `mssql_table`, `oracle_table`,
`athena_table`, `fabric_table`, `dbt_core_node`, `dbt_cloud_node`, `sql_mesh_model`,
`sql_mesh_audit`, `airflow_dag`, `airflow_task`, `monitor`, `dataproduct`, `saved_view`.
Prefer these wherever one exists for the platform.
`synq_path` is the fallback variant, for a platform with no structured form — Looker,
Tableau, Coalesce Catalog and anything added since — or for passing through an `entity_id` you
already hold.
Every identifier the API **returns** also carries `entity_id`, the entity's opaque id, and it
is the `/catalog/` segment of the entity's app URL. It is output only: on a request
it is ignored, and the structured variant is what resolves. To name an entity by opaque id on
a request, put it in the `synq_path` variant.
Ways to obtain an `entity_id`: the app URL after `/catalog/`, the
[MCP read tools](/scout/mcp), the `entity_url` in a [webhook payload](/api-reference/webhook),
or `BatchIdsByCoordinates`.
## Enums
**`SqlDialect`** — `BIGQUERY`, `CLICKHOUSE`, `DATABRICKS`, `MYSQL`, `POSTGRESQL`, `REDSHIFT`,
`SNOWFLAKE`, `DUCKDB`, `TRINO`, `MSSQL`, `ORACLE`, `ATHENA`, `FABRIC`.
**`CodeType`** — `SQL`, `PYTHON`, `JSON`, `YAML`, `DBT`, `SQLMESH`, `LOOKML`, `JAVASCRIPT`,
`BASH`, `DAX`, `JAVA`, `SCALA`, `TEXT`, `MARKDOWN`.
## Pagination
The `List*` methods on the custom services take an optional `Pagination`. Omitting it, or
sending a zero `page_size`, returns everything in one response — there is no server-side
default page size. To page, set `page_size` and pass the previous response's
`page_info.last_id` as `pagination.cursor`, until `last_id` comes back empty.
Not every list paginates: `ListEntityTypeDefs`, `ListExecutionTypeDefs` and `ListPlatformDefs`
take no pagination at all and always return the full set.
## Idempotency
Every `Upsert*` and `Delete*` method here is idempotent in **effect**: re-sending the same
state converges on the same result, and deleting something that does not exist succeeds.
It is not a no-op on the wire. An upsert always writes, restamping `updated_at` and
re-triggering the fan-out that write implies, so a re-sync is not free — send it on a schedule,
not in a tight loop.
# Building an Integration
Source: https://docs.synq.io/custom-integrations/building-an-integration
From credentials to a verified graph: the steps a custom integration runs, in order, in Go and Python
A custom integration is a program that reads your tool and writes what it finds to the
Coalesce Quality API. It is **idempotent** — running it twice produces the same estate — so
it is a cron job, not a migration.
Every snippet below is taken from the runnable examples in
[getsynq/api/examples](https://github.com/getsynq/api/tree/main/examples). Pick a language in
any code block and the rest of the page follows.
The complete program these come from is
[`bi_tool_lineage`](https://github.com/getsynq/api/tree/main/examples/golang/bi_tool_lineage)
([Python](https://github.com/getsynq/api/tree/main/examples/python/bi_tool_lineage)) — a
whole BI tool modelled end to end, run against a live workspace.
## Credentials
Create client credentials in API settings — `/settings/api` in your own app, which is
`app.synq.io` for EU, `app.us.synq.io` for US and `app.au.synq.io` for AU. Grant these scopes:
| Scope | Needed for |
| - | - |
| **Edit Entity Types** | Step 1 |
| **Edit Entities** | Steps 2, 3, 4, 6, 7 |
| **Edit Lineage** | Step 5 |
| **Edit Executions** | Step 8 |
| **Read Entities**, **Read Entity Types**, **Read Lineage** | Steps 1 and 9 |
[Getting Started](/api-reference/getting-started) covers exchanging them for an access token
and generating or installing a client — follow it rather than the short version, it is
generated from the API itself and stays current. Your region's API endpoint is in the
[region table](/security/ip).
What is specific to this guide is the environment it expects and the gRPC channel:
```bash Go theme={null}
export QUALITY_CLIENT_ID=...
export QUALITY_CLIENT_SECRET=...
export QUALITY_API_ENDPOINT=... # your region's API endpoint
go get buf.build/gen/go/getsynq/api/grpc/go
go get buf.build/gen/go/getsynq/api/protocolbuffers/go
```
```bash Python theme={null}
export QUALITY_CLIENT_ID=...
export QUALITY_CLIENT_SECRET=...
export QUALITY_API_ENDPOINT=... # your region's API endpoint
pip install grpcio protobuf requests
pip install getsynq-api-protocolbuffers-python getsynq-api-grpc-python \
--extra-index-url https://buf.build/gen/python
```
Send the token as `authorization: Bearer ` on every call and refresh it when
`expires_in` elapses. **Set the gRPC authority explicitly** — without it the port travels in
the `Host` header and every RPC returns `UNIMPLEMENTED` with no message, which reads as a
missing service rather than a routing miss:
```go Go theme={null}
import (
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
)
cfg := clientcredentials.Config{
ClientID: os.Getenv("QUALITY_CLIENT_ID"),
ClientSecret: os.Getenv("QUALITY_CLIENT_SECRET"),
TokenURL: "https://" + endpoint + "/oauth2/token",
}
conn, err := grpc.NewClient(endpoint+":443",
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
grpc.WithPerRPCCredentials(oauth.TokenSource{
TokenSource: cfg.TokenSource(ctx),
}),
// Required. Without it the port travels in the Host header and every RPC
// comes back UNIMPLEMENTED with no message.
grpc.WithAuthority(endpoint),
)
```
```python Python theme={null}
import grpc, requests, time
class TokenSource:
def __init__(self, client_id, client_secret, api_endpoint):
self.token_url = f"https://{api_endpoint}/oauth2/token"
self.client_id, self.client_secret = client_id, client_secret
self.obtain_token()
def obtain_token(self):
resp = requests.post(self.token_url, data={
"client_id": self.client_id,
"client_secret": self.client_secret,
"grant_type": "client_credentials",
})
resp.raise_for_status()
self.token = resp.json()
self.expires_at = time.time() + self.token["expires_in"]
def get_token(self) -> str:
if time.time() > self.expires_at:
self.obtain_token()
return self.token["access_token"]
class TokenAuth(grpc.AuthMetadataPlugin):
def __init__(self, source): self._source = source
def __call__(self, context, callback):
callback([("authorization", f"Bearer {self._source.get_token()}")], None)
channel = grpc.secure_channel(
f"{endpoint}:443",
grpc.composite_channel_credentials(
grpc.ssl_channel_credentials(),
grpc.metadata_call_credentials(TokenAuth(TokenSource(cid, secret, endpoint))),
),
# Required. Without an explicit authority the port travels in the Host
# header and every RPC comes back UNIMPLEMENTED with no message.
options=(("grpc.default_authority", endpoint),),
)
```
## 1. Types
One `UpsertType` per kind of thing your tool has. Declare
[traits](/custom-integrations/modelling#2-entity-types-and-their-traits) — they decide how
your entities rank, sort and behave platform-wide.
```go Go theme={null}
types := []*entitiesv1.Type{
{
TypeId: 31,
Name: "Metabase Model",
SvgIcon: iconLayers,
Traits: &entitiesv1.TypeTraits{IsModel: proto.Bool(true)},
},
{
TypeId: 32,
Name: "Metabase Question",
SvgIcon: iconChart,
// A question transforms data AND is read by a person, so it is both.
Traits: &entitiesv1.TypeTraits{IsModel: proto.Bool(true), IsBiLike: proto.Bool(true)},
},
{
TypeId: 34,
Name: "Metabase Alert",
SvgIcon: iconWarning,
// A check, not a stage data flows through.
Traits: &entitiesv1.TypeTraits{IsTestType: proto.Bool(true)},
},
}
for _, t := range types {
if _, err := api.types.UpsertType(ctx, &customv1.UpsertTypeRequest{Type: t}); err != nil {
return err
}
}
```
```python Python theme={null}
types = [
type_pb2.Type(
type_id=31,
name="Metabase Model",
svg_icon=ICON_LAYERS,
traits=type_traits_pb2.TypeTraits(is_model=True),
),
type_pb2.Type(
type_id=32,
name="Metabase Question",
svg_icon=ICON_CHART,
# A question transforms data AND is read by a person, so it is both.
traits=type_traits_pb2.TypeTraits(is_model=True, is_bi_like=True),
),
type_pb2.Type(
type_id=34,
name="Metabase Alert",
svg_icon=ICON_WARNING,
# A check, not a stage data flows through.
traits=type_traits_pb2.TypeTraits(is_test_type=True),
),
]
for t in types:
api.types.UpsertType(types_service_pb2.UpsertTypeRequest(type=t))
```
Type numbers are `1`–`1000` and workspace-wide, with no allocator. Call `ListTypes` first and
refuse to start if one of your numbers is taken under a different name — that is the only
protection against two integrations picking the same band.
## 2. Entities
One `UpsertEntity` per object. **Create every entity before anything points at one**: a SQL
binding to an entity that does not exist is rejected at the write, and an entity group whose
members do not all exist is not a set anyone can reconcile against.
```go Go theme={null}
entity := &entitiesv1.Entity{
Id: customID("metabase::question::87"),
TypeId: 32,
Name: "Revenue by product category",
Description: "Revenue split by category, last 90 days.",
Annotations: []*entitiesv1.Annotation{
{Name: "metabase.collection", Values: []string{"Commercial"}},
{Name: "metabase.kind", Values: []string{"question"}},
},
}
_, err := api.entities.UpsertEntity(ctx, &customv1.UpsertEntityRequest{Entity: entity})
```
```python Python theme={null}
entity = entity_pb2.Entity(
id=custom_id("metabase::question::87"),
type_id=32,
name="Revenue by product category",
description="Revenue split by category, last 90 days.",
annotations=[
annotation_pb2.Annotation(name="metabase.collection", values=["Commercial"]),
annotation_pb2.Annotation(name="metabase.kind", values=["question"]),
],
)
api.entities.UpsertEntity(entities_service_pb2.UpsertEntityRequest(entity=entity))
```
Where `customID` builds the identifier:
```go Go theme={null}
func customID(id string) *entitiesv1.Identifier {
return &entitiesv1.Identifier{
Id: &entitiesv1.Identifier_Custom{
Custom: &entitiesv1.CustomIdentifier{Id: id},
},
}
}
```
```python Python theme={null}
def custom_id(id_: str) -> identifier_pb2.Identifier:
return identifier_pb2.Identifier(custom=identifier_pb2.CustomIdentifier(id=id_))
```
**The id is a slug; the readable text belongs in `name`.** Keep it to a lowercase
`A-Z a-z 0-9 : _ . -` slug built from the source system's stable id. Anything else is folded
on write — `metabase/question/87` and `metabase question 87` are **the same entity** — and
ids are case-sensitive, so a capital makes a different one. `name` takes 500 characters of
any UTF-8, which is where the title, the accents and the spaces go. See
[Entity ids](/custom-integrations/modelling#1-entity-ids).
To point at an entity from a connected platform, use that platform's own identifier variant
rather than `custom`:
```go Go theme={null}
&entitiesv1.Identifier{
Id: &entitiesv1.Identifier_BigqueryTable{
BigqueryTable: &entitiesv1.BigqueryTableIdentifier{
Project: cfg.project, Dataset: cfg.dataset, Table: "order_items",
},
},
}
```
```python Python theme={null}
identifier_pb2.Identifier(
bigquery_table=identifier_pb2.BigqueryTableIdentifier(
project=cfg.project, dataset=cfg.dataset, table="order_items",
)
)
```
Where the platform has no structured variant, or you already hold the entity's opaque
`entity_id`, use the `synq_path` variant instead — see
[Finding the entity\_id](/custom-integrations/modelling#finding-the-entity-id-of-an-existing-entity).
Annotations are your tool's own filing system carried across, so the catalog can be filtered
the way the tool is browsed. Keep them to labels a person would pick out of a list; prose
belongs in the description, which accepts Markdown.
## 3. Schemas
Declare columns **before** any lineage step. A `SELECT *` expands only over an entity whose
columns are known, and a declared column edge only shows against a column the entity has.
Skip this and the table-level edges still appear, with nothing saying the column ones are
missing.
```go Go theme={null}
schema := &customfeaturesv1.Schema{StateAt: timestamppb.Now()}
for i, c := range columns {
schema.Columns = append(schema.Columns, &entitiesv1.SchemaColumn{
Name: c.name,
NativeType: c.nativeType,
Description: c.desc,
OrdinalPosition: int32(i + 1),
})
}
_, err := api.features.UpsertEntityFeature(ctx, &customv1.UpsertEntityFeatureRequest{
Feature: &customv1.Feature{
EntityId: customID("metabase::question::87"),
// One schema feature per entity, so the id is a constant. Never generate
// it — a fresh id on every run creates a new feature each time instead
// of replacing the previous one.
FeatureId: "schema",
Feature: &customv1.Feature_Schema{Schema: schema},
},
})
```
```python Python theme={null}
schema = schema_pb2.Schema(
state_at=now(),
columns=[
entities_schema_pb2.SchemaColumn(
name=c.name,
native_type=c.native_type,
description=c.desc,
ordinal_position=i + 1,
)
for i, c in enumerate(columns)
],
)
api.features.UpsertEntityFeature(
features_service_pb2.UpsertEntityFeatureRequest(
feature=features_service_pb2.Feature(
entity_id=custom_id("metabase::question::87"),
# One schema feature per entity, so the id is a constant. Never
# generate it — a fresh id on every run creates a new feature each
# time instead of replacing the previous one.
feature_id="schema",
schema=schema,
)
)
)
```
## 4. Lineage features
### SqlDefinition, with bindings
The mechanism to reach for first. Give the SQL as the tool executes it, plus the dialect and
a `database_context` for unqualified names.
A name in the SQL that is **not a warehouse object** — another custom entity, a queue, a
service — resolves to nothing and is silently dropped. `references` binds such a name to the
entity it stands for. Names that *are* warehouse objects need no binding.
```go Go theme={null}
_, err := api.features.UpsertEntityFeature(ctx, &customv1.UpsertEntityFeatureRequest{
Feature: &customv1.Feature{
EntityId: customID("metabase::question::87"),
FeatureId: "sql",
Feature: &customv1.Feature_SqlDefinition{
SqlDefinition: &customfeaturesv1.SqlDefinition{
StateAt: timestamppb.Now(),
Dialect: entitiesv1.SqlDialect_SQL_DIALECT_BIGQUERY,
Sql: question.sql,
DatabaseContext: warehouseContext(cfg),
References: []*customfeaturesv1.SqlTableReference{{
// The name exactly as the SQL writes it. Matched
// case-insensitively; leave database_name and schema_name
// empty to default them from database_context.
ObjectName: "order_items_enriched",
Entity: customID("metabase::model::31"),
}},
},
},
},
})
```
```python Python theme={null}
api.features.UpsertEntityFeature(
features_service_pb2.UpsertEntityFeatureRequest(
feature=features_service_pb2.Feature(
entity_id=custom_id("metabase::question::87"),
feature_id="sql",
sql_definition=sql_definition_pb2.SqlDefinition(
state_at=now(),
dialect=sql_dialect_pb2.SQL_DIALECT_BIGQUERY,
sql=question.sql,
database_context=warehouse_context(cfg),
references=[
sql_definition_pb2.SqlTableReference(
# The name exactly as the SQL writes it. Matched
# case-insensitively; leave database_name and schema_name
# empty to default them from database_context.
object_name="order_items_enriched",
entity=custom_id("metabase::model::31"),
)
],
),
)
)
)
```
The bindings are part of the definition and replace with it: one dropped from a later write
is gone, and the lineage it produced is withdrawn.
### ColumnLineage
For a component with no SQL at all. State each edge outright. The declaration is complete
and replaces the previous one whole, so regenerate it from your tool's metadata every run
rather than diffing.
```go Go theme={null}
var edges []*customfeaturesv1.ColumnEdge
for _, card := range dashboard.cards {
edges = append(edges, &customfeaturesv1.ColumnEdge{
Upstream: customID(fmt.Sprintf("metabase::question::%d", card.fromQuestion)),
UpstreamColumn: card.fromColumn,
// The two sides are named independently, so they need not match.
Column: card.field,
})
}
_, err := api.features.UpsertEntityFeature(ctx, &customv1.UpsertEntityFeatureRequest{
Feature: &customv1.Feature{
EntityId: customID("metabase::dashboard::9"),
// Only one column-lineage feature per entity, so a stable id is how it
// is edited.
FeatureId: "column-lineage",
Feature: &customv1.Feature_ColumnLineage{
ColumnLineage: &customfeaturesv1.ColumnLineage{
StateAt: timestamppb.Now(),
Edges: edges,
},
},
},
})
```
```python Python theme={null}
edges = [
column_lineage_pb2.ColumnEdge(
upstream=custom_id(f"metabase::question::{c.from_question}"),
upstream_column=c.from_column,
# The two sides are named independently, so they need not match.
column=c.field,
)
for c in dashboard.cards
]
api.features.UpsertEntityFeature(
features_service_pb2.UpsertEntityFeatureRequest(
feature=features_service_pb2.Feature(
entity_id=custom_id("metabase::dashboard::9"),
# Only one column-lineage feature per entity, so a stable id is how
# it is edited.
feature_id="column-lineage",
column_lineage=column_lineage_pb2.ColumnLineage(state_at=now(), edges=edges),
)
)
)
```
An upstream on a connected platform works the same way, and does not have to exist yet — the
edge is remembered and appears once the entity is ingested. Declaring a column edge also
declares the table edge it implies.
### Code and Git
`Code` carries the SQL, Python, YAML or JSON behind the entity for display; several per
entity. `GitFileReference` says where that code lives, so a connected
[code integration](/code-integrations/overview) can show recent commits against the entity
when it fails.
## 5. Relationships
For edges with nothing to say at column grain. **Read the response** — an edge is held
between the two entities the endpoints *resolve* to, so two relationships that look different
can be the same edge, and only the last write survives.
```go Go theme={null}
resp, err := api.relationships.UpsertRelationships(ctx, &customv1.UpsertRelationshipsRequest{
Relationships: []*customv1.Relationship{{
Upstream: customID("metabase::dashboard::9"),
Downstream: customID("metabase::subscription::4"),
}},
})
if err != nil {
return err
}
for _, r := range resp.GetResults() {
fmt.Printf("%s -> %s %s\n",
r.GetRelationship().GetUpstream().GetEntityId(),
r.GetRelationship().GetDownstream().GetEntityId(),
r.GetOutcome(),
)
}
```
```python Python theme={null}
resp = api.relationships.UpsertRelationships(
relationships_service_pb2.UpsertRelationshipsRequest(
relationships=[
relationships_service_pb2.Relationship(
upstream=custom_id("metabase::dashboard::9"),
downstream=custom_id("metabase::subscription::4"),
)
]
)
)
for r in resp.results:
outcome = relationships_service_pb2.RelationshipWriteOutcome.Name(r.outcome)
print(f"{r.relationship.upstream.entity_id} -> "
f"{r.relationship.downstream.entity_id} {outcome}")
```
## 6. Checks
Something that validates another entity is not a stage data flows through. Attach it with a
check relationship, **then** give it a `CheckCategory` feature — attaching schedules no
re-publish of the derived check record, while writing a feature does, so the reverse order
leaves that record stale until the entity is written again.
```go Go theme={null}
_, err := api.features.UpsertEntityFeature(ctx, &customv1.UpsertEntityFeatureRequest{
Feature: &customv1.Feature{
EntityId: customID("metabase::alert::12"),
FeatureId: "check",
Feature: &customv1.Feature_CheckCategory{
// category and governance_category are left unset on purpose: a
// producer value outranks the workspace's categorisation rules.
CheckCategory: &customfeaturesv1.CheckCategory{
Package: "metabase",
Kind: alert.kind,
},
},
},
})
_, err = api.checkRelationships.UpsertCheckRelationships(ctx,
&customv1.UpsertCheckRelationshipsRequest{
CheckRelationships: []*customv1.CheckRelationship{{
Check: customID("metabase::alert::12"),
Checked: customID("metabase::question::87"),
CheckedColumns: []string{"revenue"},
}},
})
```
```python Python theme={null}
api.features.UpsertEntityFeature(
features_service_pb2.UpsertEntityFeatureRequest(
feature=features_service_pb2.Feature(
entity_id=custom_id("metabase::alert::12"),
feature_id="check",
# category and governance_category are left unset on purpose: a
# producer value outranks the workspace's categorisation rules.
check_category=checks_pb2.CheckCategory(package="metabase", kind=alert.kind),
)
)
)
api.check_relationships.UpsertCheckRelationships(
checks_relationships_service_pb2.UpsertCheckRelationshipsRequest(
check_relationships=[
checks_relationships_service_pb2.CheckRelationship(
check=custom_id("metabase::alert::12"),
checked=custom_id("metabase::question::87"),
checked_columns=["revenue"],
)
]
)
)
```
**Leave `category` and `governance_category` unset.** They are resolved by your workspace's
own [categorisation rules](/analytics/check-categories), and a value sent by a producer
outranks those rules. Deriving one from your tool's alert kind — the obvious thing to do —
silently switches off every rule the workspace wrote, and nothing in the product explains
why.
## 7. The entity group
One group naming **every entity your integration owns**, every run. The server diffs it
against the previous set and deletes what is missing, so an object deleted in your tool
disappears from the catalog with no state kept on your side.
```go Go theme={null}
ids := everyEntity() // the FULL set, every run
resp, err := api.groups.UpsertEntitiesGroup(ctx, &customv1.UpsertEntitiesGroupRequest{
Group: &customv1.Group{GroupId: "metabase", EntityIds: ids},
})
if err != nil {
return err
}
for _, deleted := range resp.GetDeletedIds() {
fmt.Printf("deleted (gone from the tool): %s\n", deleted.GetCustom().GetId())
}
```
```python Python theme={null}
ids = every_entity() # the FULL set, every run
resp = api.groups.UpsertEntitiesGroup(
groups_service_pb2.UpsertEntitiesGroupRequest(
group=groups_service_pb2.Group(group_id="metabase", entity_ids=ids)
)
)
for deleted in resp.deleted_ids:
print(f"deleted (gone from the tool): {deleted.custom.id}")
```
**A partial send is a deletion of everything omitted.** If reading your tool can fail
halfway, skip the group write for that run and let the next complete one reconcile.
**A group deletes entities only** — not the features, relationships or check relationships of
entities that still exist. A repointed dashboard, a removed check or a stale `SqlDefinition`
survives the group write and stays in the graph.
So each run also reconciles what the last one wrote: read the current state with
`ListEntityFeatures` (leave `entity_id` unset for the whole workspace), `ListRelationships` and
`ListCheckRelationships`; diff it against what you are about to write; then call
`DeleteEntityFeature`, `DeleteRelationships` and `DeleteCheckRelationships` for what is gone —
**before** the group write, so no edge is left pointing at an entity that is about to go.
## 8. Executions
What gives a custom entity a status rather than just a position in a graph. `created_at` is
required and must be in the past, so a run that has not happened yet is simply not reported.
```go Go theme={null}
_, err := api.executions.UpsertExecution(ctx, &customv1.UpsertExecutionRequest{
Execution: &customv1.Execution{
Id: customID("metabase::dashboard::9"),
Status: customv1.ExecutionStatus_EXECUTION_STATUS_OK,
Message: "All cards refreshed.",
CreatedAt: timestamppb.New(finished),
StartedAt: timestamppb.New(started),
FinishedAt: timestamppb.New(finished),
},
})
```
```python Python theme={null}
api.executions.UpsertExecution(
entity_executions_service_pb2.UpsertExecutionRequest(
execution=entity_executions_service_pb2.Execution(
id=custom_id("metabase::dashboard::9"),
status=entity_executions_service_pb2.EXECUTION_STATUS_OK,
message="All cards refreshed.",
created_at=finished,
started_at=started,
finished_at=finished,
)
)
)
```
Status is `OK`, `WARN`, `ERROR` or `CRITICAL`. `UpsertLogEntry` attaches log output without
changing status.
## 9. Read it back
Every way a custom integration goes wrong produces a valid-looking write and an empty graph,
and the write side cannot tell the difference. End the program by reading its own lineage
back. Lineage is computed asynchronously, so poll.
```go Go theme={null}
depth := int32(10)
resp, err := api.lineage.GetLineage(ctx, &lineagev1.GetLineageRequest{
LineageDirection: lineagev1.LineageDirection_LINEAGE_DIRECTION_UPSTREAM,
StartPoint: &lineagev1.GetLineageStartPoint{
From: &lineagev1.GetLineageStartPoint_Entities{
Entities: &lineagev1.EntitiesStartPoint{
Entities: []*entitiesv1.Identifier{customID("metabase::dashboard::9")},
},
},
},
MaxDepth: &depth,
})
if err != nil {
return err
}
lin := resp.GetLineage()
if len(lin.GetNodeDependencies()) == 0 {
return fmt.Errorf("no upstream lineage: the writes landed but nothing resolved")
}
for _, d := range lin.GetNodeDependencies() {
fmt.Printf("%s -> %s\n",
nodeName(lin.GetNodes()[d.GetSourceNodeIdx()]),
nodeName(lin.GetNodes()[d.GetTargetNodeIdx()]),
)
}
```
```python Python theme={null}
resp = api.lineage.GetLineage(
lineage_service_pb2.GetLineageRequest(
lineage_direction=lineage_direction_pb2.LINEAGE_DIRECTION_UPSTREAM,
start_point=lineage_service_pb2.GetLineageStartPoint(
entities=lineage_service_pb2.EntitiesStartPoint(
entities=[custom_id("metabase::dashboard::9")]
)
),
max_depth=10,
)
)
lin = resp.lineage
if not lin.node_dependencies:
raise RuntimeError("no upstream lineage: the writes landed but nothing resolved")
for d in lin.node_dependencies:
print(f"{node_name(lin.nodes[d.source_node_idx])} -> "
f"{node_name(lin.nodes[d.target_node_idx])}")
```
A node carries every identifier that resolves to it, so `nodeName` picks the shape that says
what the node *is* — prefer the `custom` variant when present.
Check for the edges you expect, not merely that some edge came back. A declared column edge
naming a column the upstream does not have still produces one hop while the rest of the chain
is lost, so a non-empty result can still be a broken graph. Poll with a deadline, and exit
non-zero when it expires rather than logging and returning.
For column-level lineage, start from `EntityColumnsStartPoint` instead, naming the entity and
the columns. The response then populates `column_dependencies`, and each node's
`cll_details.cll_state` says whether the parse succeeded. A state of `RESOLUTION_FAILED` on a
node is usually a name your `SqlDefinition` should have bound and did not.
This is not a first-version-only step. Keep it in the production integration.
## Checklist before you ship
* Ids are lowercase slugs from the source system's stable id, in `A-Z a-z 0-9 : _ . -`
* The title and any other human-readable text is in `name`, never in an id
* Every type declares its traits, and its number was checked with `ListTypes` first
* Every entity with columns declares its schema, before any lineage is written
* Every name in a `SqlDefinition` that is not a warehouse object has a binding
* Every upstream resolves, checked with `BatchResolveIdentifiers` (`check_existence: true`)
* Check entities send `package` and `kind`, and leave both categories unset
* One group holds everything, sent whole on every run, never from a partial read
* `feature_id` values are stable strings, not generated per run
* The program reads its own lineage back and fails loudly when it is missing
## Next
Every method, the scope it needs, and the limits
A page to point a coding agent at, so it writes the integration for you
# Modelling Your Tool
Source: https://docs.synq.io/custom-integrations/modelling
Choosing entity types, traits and the right lineage mechanism for each hop in your tool's graph
Before writing any code, four decisions. They are listed in order of how expensive they are
to get wrong.
## 1. Entity ids
Every entity is named by a `CustomIdentifier` — one string, unique within your workspace.
**Treat it as a slug, not a label.** It is a machine identifier: something a URL, a log line
and a config file can all carry unchanged. Everything a person reads goes in `name` and
`description`, which are separate fields and have no such restriction.
```
id metabase::question::87
name Revenue by product category — EMEA, Q3
```
The id never has to carry the title. The catalog shows `name`, which takes up to 500
characters of any UTF-8 you like: accents, punctuation, spaces, the tool's own wording. The
id only has to be stable and unique.
### Building the slug
Two rules, and a third worth adopting.
**Build it from the source system's stable id, never from a title.** Everything hangs off the
id: lineage edges, schemas, executions, check relationships, ownership, a saved view somebody
built. Changing an id does not rename an entity — it creates a second one and orphans the
first. A title changes; a primary key does not.
**Restrict it to `A-Z a-z 0-9 : _ . -`.** That is the alphabet the platform preserves
verbatim. `::` is its conventional separator, and `.`, `-` and `_` pass through untouched.
A prefix naming the tool and the object kind keeps two integrations in one workspace from
colliding, and keeps ids legible in a URL.
**Lowercase everything.** Ids are case-sensitive, so `Metabase::Question::87` and
`metabase::question::87` are two separate entities. Nothing warns you. Lowercasing at the one
place you build ids removes the whole class of bug, and costs nothing — the readable form
lives in `name` regardless.
```
metabase::question::87 good — stable id, safe alphabet, lowercase
metabase::Revenue by category bad — a title, a space, and a capital
```
### What happens if you ignore this
The string you send is not stored verbatim. Three things happen to it, in order:
1. It is truncated at **200 bytes**, with the tail replaced by a hash of the whole input so
two long ids sharing a prefix do not collapse into one.
2. Non-ASCII characters are replaced by their literal escape text, so `zamówienia` becomes
`zam_u00f3wienia`.
3. Every character outside `A-Z a-z 0-9 : _ . -` is folded to `_`.
**Two ids that differ only in folded characters become the same entity.** `metabase/question/87`
and `metabase question 87` both normalise to `metabase_question_87` — one entity, silently,
with the second write overwriting the first.
Slashes, spaces, `#`, `@`, `+` and `~` all fold. So does every accented character, into an
escape sequence nobody wants to read. Put that text in `name`, where it belongs and where it
survives intact.
The stored id is your slug prefixed with `custom-`, and that is what the API returns as
`entity_id`:
```
you send metabase::question::87
stored as custom-metabase::question::87
```
### Finding the entity\_id of an existing entity
Custom lineage points at entities that already exist — warehouse tables, dbt models,
dashboards.
**Always prefer the structured variant for the platform**: `bigquery_table`,
`snowflake_table`, `dbt_core_node` and the rest name an entity in the terms the platform
itself uses, which is what makes them readable, stable and checkable.
`synq_path` is the fallback. Reach for it in two cases: the platform has no structured
variant (Looker, Tableau, Coalesce Catalog and anything added since are addressable only this
way), or you already hold the entity's opaque `entity_id` and would rather pass it through
than take it apart. Four ways to get one:
* **From the app URL.** The catalog page for an entity is
`/catalog//overview`, where the app host is your region's
(`app.synq.io`, `app.us.synq.io`, `app.au.synq.io`). In
`https://app.synq.io/catalog/ch-nogwv291ou.europe_west4.gcp.clickhouse.cloud::default::runs/overview`
the id is `ch-nogwv291ou.europe_west4.gcp.clickhouse.cloud::default::runs`.
* **From the MCP server.** `search_entities` and the other read tools return entities with
their ids — see [Scout MCP](/scout/mcp).
* **From a webhook payload.** Every entity in an event carries its `identifier` and an
`entity_url` whose `/catalog/` segment is the id — see [Webhooks](/api-reference/webhook).
* **From the API.** Every identifier the API *returns* carries `entity_id`, and
`BatchIdsByCoordinates` turns a warehouse name into the ids that match it.
`entity_id` is output only. On a request it is ignored and the structured variant is what
resolves, so to name an entity *by* its opaque id, set the `synq_path` variant to that string.
### Resolve identifiers before you write lineage
`BatchResolveIdentifiers` (`synq.entities.resolve.v1`) turns any identifier — of any shape —
into its `entity_id`, and with `check_existence: true` tells you whether the entity is
actually there.
**The two kinds of upstream behave differently, and this is what tells them apart:**
* A **custom** entity that does not exist is **rejected** at the write. Nothing else would
report a misspelled custom id, so the API refuses rather than accepting a declaration that
produces nothing.
* An entity on a **connected platform** is not checked. It binds when that platform is next
ingested, so an edge written before the table is crawled is correct and is remembered.
That second case is the one to resolve up front: a not-yet-ingested table and a typo'd
warehouse name are indistinguishable from the lineage you get back.
It also answers a question the write API cannot. A single entity is reachable by several
identifiers — a dbt model and the warehouse table it builds are **one identity**, named two
ways. `BatchResolveIdentifiers` returns the whole identity group in `identities` and
`identity_synq_paths`, so you can tell that two identifiers you were treating as different
upstreams are the same entity, before writing two relationships that turn out to be one edge.
### The read API is input, not just verification
A custom integration does not only write. The public API's read surface is there to be driven
from your integration, and using it is usually the difference between a mapping that guesses
and one that resolves:
* **`BatchIdsByCoordinates`** — your tool reports a table as a string like
`analytics.order_items`. This maps warehouse coordinates to the entity ids that match, so
you can bind a name without hardcoding which warehouse it lives in.
* **`SearchEntities`** and **`ListEntities`** — find the entity your tool's metadata refers
to when it gives you a name rather than an address.
* **`GetSchema`** — read an upstream's real columns, so the column lineage you declare names
columns that exist rather than columns your tool believes exist.
* **`ListEntityTypeDefs`** and **`ListTypes`** — what types the workspace already has, which
is how you avoid claiming a type number another integration is using.
* **`Browse`** and **`GetFolderOf`** — where entities sit in the workspace's folder tree.
Treat these as the configuration input for the mapping, resolved at run time, rather than
building a static table of ids into your integration and watching it drift.
## 2. Entity types and their traits
A type is a kind of thing in your tool. It carries a short name, an SVG icon, and a set of
**traits** that every entity of the type inherits.
| Trait | Declares that entities of this type are | Effect |
| - | - | - |
| `is_model` | transformation models, the dbt-model analog | Canonical-entity ranking, monitorability, lineage treated as a transformation stage |
| `is_source` | raw external inputs to the graph | Ranked and rendered as a source |
| `is_bi_like` | dashboards, reports, other BI artifacts | Treated as a consumption endpoint |
| `is_test_type` | checks, pairing with declarative checks | Participates in coverage and check reporting |
| `is_ignored_in_lineage` | infrastructure you do not want drawn | Excluded from lineage rendering |
Traits are the difference between a catalog that understands your tool and one that merely
lists it. A type with no traits still appears everywhere, but it does not rank against dbt
models when Coalesce Quality picks a canonical entity, and it will not be offered where a
model is expected.
Types are numbered `1`–`1000` per workspace, and the number is the identity — reusing one
redefines the type. Keep a table of them in your integration's source.
## 3. Which lineage mechanism
This is the decision that repays thinking. Coalesce Quality offers four, and the right one
depends on what the hop actually has.
| You have | Use | Why |
| - | - | - |
| SQL, over real warehouse tables | `SqlDefinition` | Both table and column grain are derived from the SQL, and stay correct when the SQL changes |
| SQL, naming something the warehouse cannot address | `SqlDefinition` + `references` | The binding says what the name means; everything else is derived as usual |
| Rows copied between two tables you already have (replication, CDC, a loader) | An entity for the copy, with `SqlDefinition` + `references` | Column grain across the hop, even when the two tables are in different warehouses |
| No SQL, but you know the column mapping | `ColumnLineage` | Declare the answer instead of deriving one |
| No SQL and no columns | `Relationship` | The edge is real; there is nothing to say at column grain |
| Something that validates another entity | `CheckCategory` + a check relationship | A check is not a stage data flows through |
**Reach for `SqlDefinition` first.** A declaration is a snapshot of what was true when it
was written; a parse follows the query. Every other mechanism is for what SQL cannot say.
### SqlDefinition
Give the SQL as the tool actually executes it, plus the dialect. Coalesce Quality parses it
and derives table-level and column-level lineage.
```
dialect: SQL_DIALECT_BIGQUERY
sql: SELECT customer_id, SUM(net_revenue) AS revenue
FROM analytics.order_items_enriched
GROUP BY 1
```
`database_context` supplies the instance, database and schema that unqualified names in the
SQL resolve against — the same defaults the warehouse itself would apply.
### SqlDefinition references — the binding
A `SqlDefinition` resolves names that live at a warehouse address. Anything else — another
custom entity, a queue, a service, an API, a saved query in a BI tool — has no address, so
`FROM order_items_enriched` matches nothing and is dropped.
**There is no error for this.** An empty result looks exactly like a query that genuinely
reads nothing, which is why it is the most common way a custom integration ships broken.
`SqlDefinition.references` binds the name, as the SQL writes it, to the entity it stands
for. Three consequences:
* **A binding beats the warehouse.** If a real table shared the name, the binding still
wins — which is what makes it usable as a deliberate override.
* **Empty parts default from `database_context`**, exactly as an unqualified name in the
SQL does. A binding of just `object_name: "orders_topic"`, under a definition whose
database is `kafka`, matches both `FROM kafka.orders_topic` and `FROM orders_topic`.
* **Names that *are* warehouse objects need no binding.** One definition can read a bound
custom entity and a raw warehouse table in the same query, binding only the first.
Matching is case-insensitive. The bindings are part of the definition and replace with it:
a binding dropped from a later write is gone, and the lineage it produced is withdrawn.
### A copy between two tables you already have
A replication tool, a CDC pipeline or your own loader copies a table into another database
or warehouse. Both tables are already in Coalesce Quality; the hop between them is not.
Model the copy itself as a custom entity, **one per copied table**. It is what fails, lags or
drops a column, so it is where an incident belongs. One entity for a whole connection would
make every source table upstream of every target table it writes.
Give that entity a `SqlDefinition` describing the copy, and bind **both** table names:
```
dialect: SQL_DIALECT_POSTGRESQL
sql: INSERT INTO target_table (id, customer_id, total_amount)
SELECT id, customer_id, total FROM source_table
references: source_table -> the source table's identifier
target_table -> the target table's identifier
```
* **The SQL is parsed, never run.** `source_table` and `target_table` are placeholders that
only have to match the references. A bound name resolves straight to its entity, on
whichever platform that is, so the source and the target can be in different warehouses.
Use the source side's dialect.
* **Write the column list on the `INSERT`.** It pairs each selected column with its target
column, renames included. The lineage runs source column → copy → target column.
* **No relationships are needed.** The same definition gives the table-level edges too.
[`replication_lineage`](https://github.com/getsynq/api/tree/main/examples/golang/replication_lineage)
is a runnable example.
### ColumnLineage
For a component with no SQL for a parser to read — a transformation written in Python, a
hand-maintained mapping, a dashboard field assembled by the tool's own engine. You state
each edge: upstream entity, upstream column, this entity's column.
```
upstream = metabase::question::87, upstream_column = revenue, column = revenue
```
Three things to know:
* **It replaces whole.** An edge missing from a write is withdrawn, and deleting the
feature withdraws all of them. Only one `ColumnLineage` per entity, so re-writing it
under the same `feature_id` is how it is edited.
* **A column edge declares the table edge it implies.** The entity appears downstream of
each upstream named here whether or not a `Relationship` also says so.
* **It wins over `SqlDefinition`.** An entity with both is served from the declaration in
the SQL-lineage view — an explicit statement outranks a derived one. So declare here only
what the SQL cannot say, or state the entity's whole column lineage here and treat it as
the authority.
`state_at` defaults to now; set it explicitly when backfilling so an older declaration does
not overwrite a fresher one.
### Relationship
A plain upstream/downstream edge between two entities, with nothing said at column grain.
Use it where there genuinely are no columns — a subscription, an export job, a notification.
Relationships join a custom entity to another custom entity, or a custom entity to a native
one. **Two native entities cannot be joined** — their lineage comes from their own
integrations. When your tool is what moves data between two of them, model it as described
in [A copy between two tables you already have](#a-copy-between-two-tables-you-already-have).
An edge is held between the two *entities* the endpoints resolve to, not between the
identifiers as written, and one entity accepts several spellings. The write response reports
the resolved pair and what the write did (`CREATED`, `UPDATED`, `DELETED`, `NOT_FOUND`) —
read it rather than assuming, because two relationships that resolve to the same pair are
one edge and only the last write survives.
### Checks
Something that validates another entity is not a stage data flows through, so it does not
get a lineage edge. Give the entity a `CheckCategory` feature and attach it with a **check
relationship**, optionally naming the columns it covers.
```
package: "metabase" grouping of check kinds
kind: "alert" what kind of check this is, in your tool's own words
```
**Write the check relationship before the `CheckCategory` feature.** Attaching a check
schedules no re-publish of its derived record, while writing a feature does — so doing it the
other way round leaves the check's stored state stale until the entity is written again.
**Leave `category` and `governance_category` unset.** They are resolved by your workspace's
own [categorisation rules](/analytics/check-categories), and a value sent by a producer
outranks those rules. Deriving one from your tool's alert kind — the obvious thing to do —
silently switches off every rule the workspace wrote, and nothing in the product explains
why. Send `package` and `kind`, which is what your tool actually says, and let the rules
decide.
## 4. Declare the schema, or lose the column grain silently
Declare every entity's columns before anything reads them. This is the step most easily
skipped and the most expensive to skip: a `SELECT *` over a bound entity expands only over an
entity whose columns are known. Skip it and the table-level edges still appear, with only the
column-level ones missing and nothing saying so.
**A declared `ColumnLineage` edge is not checked against the upstream's schema.** The read
joins on the column names the two sides state, so a typo in `upstream_column` is accepted and
produces a broken hop rather than an error — the edge exists, and the chain beyond it does
not. Declaring the schema does not protect you here; reading the lineage back is what catches
it.
Columns carry a name, a native type, a description, an ordinal position, and may nest for
struct and repeated types. Give ordinal positions for all columns or for none — when absent
they follow list order.
## Reading the graph back
Every way this goes wrong produces a valid-looking write and an empty graph. The two are
indistinguishable from the write side, so a custom integration ends by reading its own
lineage back and checking it is there. Lineage is computed asynchronously, so the read
polls.
This is worth keeping in the production integration, not just in the first version.
## Next
The walkthrough, in order, with the code
Every method, scope and limit
# Custom Integrations
Source: https://docs.synq.io/custom-integrations/overview
Put a tool Coalesce Quality has no integration for into the catalog yourself, with lineage, schemas, checks and run status
Coalesce Quality ships integrations for warehouses, dbt, SQLMesh, orchestrators, BI tools
and catalogs. This section is about everything else: a tool you built in-house, one you are
piloting, a streaming platform, a reverse-ETL job, a machine learning pipeline, an internal
service that reads a table and writes a file.
You describe it over the public API, and it becomes a first-class part of the catalog — it
appears in lineage, carries a schema, has a status, is covered by checks, and can be owned,
alerted on and put in a data product like anything else.
## What you can express
| Concept | What it is |
| - | - |
| **Entity type** | A kind of thing in your tool — `Kafka Topic`, `Feature View`, `Notebook`. Carries an icon and behavioural traits |
| **Entity** | One instance of that kind: a slug id for machines, plus a name, description and annotations for people |
| **Schema** | The columns the entity has, so column-level lineage and column-level checks can reach it |
| **Lineage** | What flows into it: derived from SQL, declared column by column, or stated as a plain edge |
| **Executions** | The runs of the entity, so it has a health status rather than just a position in a graph |
| **Checks** | Entities that validate other entities, attached to what they check |
| **Code** | The SQL, Python or configuration behind the entity, and the file in Git it lives in |
| **Group** | The set of entities your integration owns, which is what makes deletion work |
## When you need one
Reach for a custom integration when the thing you care about is **not addressable in a
warehouse**. If it has a table name, Coalesce Quality can already see it through the
warehouse integration, and the more direct routes are the ones to use:
* A **dbt** or **SQLMesh** project — use the [dbt](/dbt-integrations/dbt-core) or
[SQLMesh](/sqlmesh-integrations/sqlmesh) integration.
* **Airflow**, or any orchestrator that speaks OpenLineage — use the
[orchestration integrations](/orchestration-integrations/airflow).
* A **BI tool we support** — [Looker](/bi-integrations/looker),
[Tableau](/bi-integrations/tableau), [Omni](/bi-integrations/omni).
* **Tests you already run in the warehouse** — [SQL tests](/monitors/sql-tests) express
those without any code.
Everything else is a custom integration: a BI tool we have no connector for, a Kafka topic,
an S3 export, a feature store, an internal API that serves a dataset, a Python job that
transforms data outside SQL.
## What it looks like when it works
A field on a dashboard that runs no SQL at all, in a tool with no integration, traced back
to the physical warehouse columns it came from:
```
dashboard.revenue_by_category.revenue
<- question.revenue
<- model.net_revenue
<- order_items.quantity
<- order_items.unit_price
<- order_items.discount
```
Three of those four hops use a different mechanism, because each hop has different
information available. Choosing correctly per hop is the part worth thinking about;
[Modelling your tool](/custom-integrations/modelling) is the decision.
## Where to go next
Which lineage mechanism for which hop, what traits do, and the two mistakes that produce
a valid-looking write and an empty graph
The walkthrough: credentials, types, entities, features, groups, executions, verification
Every service and method involved, the scope each one needs, and the limits
Runnable Go and Python programs, including a whole BI tool modelled end to end
## Support
Questions about modelling a tool of your own go to your Technical Account Manager — see
[Support](/support/support).
# BI Status Badge
Source: https://docs.synq.io/data-products/bi-badge
Often, the best way to notify end-users of issues is directly in the tools they use on a daily basis. With BI status badges, you can display if there are any issues on or upstream of a chart directly in your BI tool.
### Creating a BI badge
* Navigate to [Products](https://app.synq.io/products)
* Select the data product that contains the assets that you want to display the status of in your BI tool and navigate to the `settings` tab
* Depending on your BI tool, copy the relevant Markdown or SVG URL
* Navigate to your BI tool and insert a widget that points to the URL above. The level of support for this may vary depending on the BI tool you use
**Looker example**
If you use Looker, click `Add > Markdown` and paste the Markdown URL from Coalesce Quality into the `Body`
* Your BI tool will show a tile with the status of the data product. Clicking on the tile brings you back to Coalesce Quality with an overview of the issues to connect the dots.
# Creating a data product
Source: https://docs.synq.io/data-products/creating-a-data-product
### Setting up a new data product
1. Navigate to [Products](https://app.synq.io/products) and click `Create product`
2. Name your data product (e.g., marketing KPIs) and set a priority (i.e., `P-1` for business-critical data)
3. Assign a folder to the data product. This is useful for grouping related data products (optional)
4. Use the `Assets selection` to find relevant assets. Use the browser to select or search for specific assets or annotations to select assets based on code definitions.
### Setting ownership of a data product
The Data Product owner is a person or team responsible for the data product. Owners can see an overview of everything they are responsible for and understand who the owners of critical data are downstream.
1. `Choose an owner` or `Create a new owner` to assign ownership to the data product. *When you create an owner, you also set how owners are notified of issues (e.g., which Slack channel)*
### Setting alerts on a data product
1. Toggle `Setup alerts` to active alerts for issues on the data product. Owners of the data product are notified based on the Slack or Email settings you’ve defined for the owner (see [Ownership overview](/ownership/ownership-overview))
* **Severity**—choose which type of issues alerts will include (e.g., check `warn` to be notified of warn-level test issues from dbt, SQLMesh, or Coalesce Transform)
* **Upstream issues**—check if you want to receive alerts about upstream issues that could be impacting your data product
* **Ongoing issues**—get notified about ongoing issues to ensure they get resolved (will result in more alerts)
### Creating a data product programmatically
The asset selection you build in the wizard is a membership query, and it can be
written directly rather than clicked.
* **Public API** — `synq.dataproducts.v2.DataproductsService` lists products,
fetches one with its membership definition, resolves the assets currently in
it, and upserts or deletes it. Reading needs a token with
`SCOPE_DATAPRODUCT_READ`, writing needs `SCOPE_DATAPRODUCT_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [data product tools](/scout/mcp#data-products) let an AI
assistant review what exists, draft a membership query, and save the product
once you approve. Reading is covered by read-only consent; saving and deleting
need the Governance permission and are confirmation-gated.
# Data products overview
Source: https://docs.synq.io/data-products/data-products-overview
Coalesce Quality enables you to deploy relevant monitors, track uptime, and manage ownership of your business-critical data assets through Data Products
You should consider data products based on downstream use cases of your most important data. By setting up data products, you can head to `Products` for a complete overview of your products and their status.
*Data products showing the priority, owner, and status of all monitors and tests*
*Examples of data products are*
A set of transformation models and metrics within a specific folder, like a finance mart (e.g., dbt models, SQLMesh models, or Coalesce nodes).
A group of transformation models linked by an exposure, for instance, models used by a CLTV model that powers marketing automation.
A selected collection of dashboards in a BI tool, such as core KPI reporting.
A compilation of tables or transformation models that carry a common tag, like all 'P1' models that require high-priority management.
### Benefits of creating data products
Data products give you an easy way to manage the end reliability of your business-critical data.
* **Understand end-end health**—understand all monitors and tests on the data product and its upstream dependencies.
* **Set relevant monitors**—one-click monitor setup based on your data product definitions.
* **Define and activate ownership**—manage ownership and alerting on the data product level.
* **Communicate uptime & SLAs**—see historical issues and share insights around quality scores and test coverage of the data product.
### Understanding a data product
Data products are simple on the surface but complex under the hood. The key concepts to understand around a data product are `lineage`, `assets`, `definitions`, `historical uptime`, `product health`, and `upstream health`.
A Data Product has four sections:
1. **Overview**-key information about the data product assets, current status, and health over the past 15 days.
2. **Issues**-list all the issues that are currently affecting your data product.
3. **Lineage**-the relationships between your product's assets.
4. **Settings**-modify your product's name, alert settings, owner, and assets.
Data Product overview:
* **Summary**—platforms, priority, description, and a summary of the quality score and coverage
* **Related incidents & issues**—shows all issues and incidents on or upstream of the data product
* **Lineage**—data assets and data products up and downstream of the data product
* **Assets**—all data assets that are part of the data product definition
### Creating a data product
1. Go to [Creating a data product](/data-products/creating-a-data-product)
### Modifying or deleting a data product
1. Navigate to [Products](https://app.synq.io/products)
2. Click the three dots for the data product you want to modify and select `Settings` to edit it or `Delete` to delete it.
# dbt Cloud
Source: https://docs.synq.io/dbt-integrations/dbt-cloud
Integrating dbt Cloud with Coalesce Quality
This guide will walk you through how to create a service token and set up the relevant configurations for your dbt Cloud project
We need this information so we can connect to dbt and extract metadata such as runs and test failures. We only request read access to your metadata and won’t be able to read raw data or update or run any models.
In order to be able to finish this guide you’ll need:
→ Access to dbt cloud
→ Having been assigned the owner role in dbt cloud
⏱️ Estimated time to finish: 10 minutes.
## Name your integration
For example `dbt cloud`
## Set the API endpoint
The dbt Cloud default region is in the US but if you’re on the enterprise plan you can host your project in a different region. Select the region where your dbt Cloud project is hosted
* **Production (US):** [https://cloud.getdbt.com](https://cloud.getdbt.com) (*default*)
* **Production (Europe):** [https://emea.dbt.com](https://emea.dbt.com)
* **Production (AU):** [https://au.dbt.com](https://au.dbt.com)
## Generate a token
These steps will take you through how to create a token you can use to manage Coalesce Quality access to your dbt Cloud project
1. Open dbt cloud and go to **Account Settings** in the menu bar
* *If you don’t see this option in the sidebar you don’t have the right dbt permission. If this is the case, either ask your dbt admin to make you an **Owner** (expand to see how) or ask the dbt admin to complete the steps in this guide*
2. Click **Service Tokens**
3. Create **New Token**
4. **Name** your token
*This could be synq\_token so you can identify it later*
5. Click **Add** to add a new permission
6. Set permissions
* `Permission Set` should be set to **Read-Only.** This will let Coalesce Quality read your dbt logs
* `Project` should be set to your main dbt project. If you want to integrate multiple projects, add all projects you want to integrate or set to All Projects
7. **Save** the token
8. **Copy** the token value
Make sure to write the token id down as you will not be able to access it again afterwards
## Select your dbt project
Choose your main dbt project
## Select the job(s) you want Coalesce Quality to monitor
You’ll most likely want to monitor your production and freshness job(s). Unless you have a good reason, we suggest you don’t monitor jobs such as CI, staging, or experimental jobs
# FAQ
### What access do you require?
We only request read access to your metadata and won’t be able to read raw data or update or run any models
### Will you store personal or business-critical data
Coalesce Quality only stores log level data and doesn’t access any actual data from your data warehouse
### Which artifacts are you collecting from dbt
We are collecting the following artifacts from dbt. For more information about dbt artifacts see [this article](https://docs.getdbt.com/docs/dbt-cloud/using-dbt-cloud/artifacts)
* manifest.json — to understand the structure of the warehouse
* run\_results.json — to see the status of executions
* catalog.json — to be able to provide catalog-like functionality
* Optionally sources.json — to capture dbt source freshness
# dbt Core
Source: https://docs.synq.io/dbt-integrations/dbt-core
Integrating dbt Core with Coalesce Quality
This guide shows you how to connect your dbt Core project to Coalesce Quality to track model runs, test results, and metadata changes.
**Prerequisites:**
* Admin access to your Coalesce Quality workspace
* Ability to modify your dbt orchestration tool (Airflow, GitHub Actions, etc.)
⏱️ **Estimated time:** 15 minutes
Using dbt Cloud? You can integrate directly through **Settings → Integrations → Add Integration → dbt Cloud** instead of following this guide.
# Set up dbt Core integration
## Create integration in Coalesce Quality
1. Navigate to **Settings → Integrations → Add Integration**
2. Select **dbt Core** from the integration options
### Configure integration settings
**Integration name:** Enter a descriptive name (e.g., `Production dbt Core`)
**Generate token:** Click **Create** to generate your integration token. You'll use this token with the `synq-dbt` tool to send artifacts securely to Coalesce Quality.
**Git integration:** Select your Git provider to link model changes to repository commits. This enables change tracking and lineage visualization.
**Relative path to dbt:** If your dbt project isn't in the repository root, specify the directory path (e.g., `analytics/dbt/`).
## Manage integration tokens
Access token management through **Settings → Integrations**, then select your dbt Core integration and click **Manage tokens**.
From the token management screen, you can:
* Create new tokens for different environments
* Invalidate compromised tokens
* Copy token snippets for easy integration
# Install synq-dbt
## About synq-dbt
[`synq-dbt`](https://github.com/getsynq/synq-dbt) is a command-line wrapper that runs your existing dbt Core commands and automatically uploads artifacts to Coalesce Quality. It's version-agnostic — working with any dbt Core version by passing all arguments directly to your installed dbt — and integrates seamlessly with orchestration tools like Airflow, GitHub Actions, and Dagster.
**Collected artifacts:**
* `manifest.json` — Project structure and dependencies
* `run_results.json` — Execution status and performance metrics
* `catalog.json` — Complete data warehouse schema information
* `sources.json` — Source freshness test results
`catalog.json` is only written by `dbt docs generate`. `dbt run`, `dbt test` and `dbt build` don't produce it. See **Send catalog.json** below.
**How it works:**
1. Executes your locally installed dbt Core with all provided arguments (version-agnostic, passes arguments directly)
2. Captures the original dbt exit code
3. Reads your `SYNQ_TOKEN` environment variable
4. Uploads artifacts from the target directory to Coalesce Quality
5. Returns the original dbt Core exit code, even if upload fails (preserving pipeline behavior and ensuring CI/CD reliability)
## Installation methods
Choose the installation method that matches your dbt orchestration setup:
### Airflow with DockerOperator
1. **Set environment variable:** In Airflow UI, create a new environment variable `SYNQ_TOKEN` with your integration token.
2. **Update Dockerfile:**
```dockerfile theme={null}
RUN wget -O /usr/bin/synq-dbt https://github.com/getsynq/synq-dbt/releases/latest/download/synq-dbt-amd64-linux && \
chmod +x /usr/bin/synq-dbt
```
`latest/download` always resolves to the newest release. To pin an image to a known
version instead, replace it with `download/v2.1.0` — the tags are on the
[releases page](https://github.com/getsynq/synq-dbt/releases).
3. **Update your operator:**
```python theme={null}
KubernetesPodOperator(
env_vars={
"SYNQ_TOKEN": Variable.get("SYNQ_TOKEN"),
# US: "SYNQ_API_ENDPOINT": "https://api.us.synq.io"; AU: "https://api.au.synq.io"
},
cmds=["synq-dbt"],
arguments=["build"], # Your dbt command here
# ... other configuration
)
```
**Linking dbt models to Airflow tasks:** To automatically link your dbt models with the Airflow tasks that execute them, see the [Airflow + dbt Core Linking guide](/orchestration-integrations/airflow-dbt-core). This enables bidirectional visibility between your orchestration and data layers.
### Airflow with dbt Plugin
1. **Set environment variable:** Create `SYNQ_TOKEN` in Airflow UI.
2. **Install synq-dbt:**
```shell theme={null}
wget -O ./synq-dbt https://github.com/getsynq/synq-dbt/releases/latest/download/synq-dbt-amd64-linux
chmod +x ./synq-dbt && mv synq-dbt /usr/local/bin/synq-dbt
```
3. **Update DbtOperator:**
```python theme={null}
dbt_run = DbtRunOperator(
dbt_bin='synq-dbt',
env={
"SYNQ_TOKEN": Variable.get("SYNQ_TOKEN"),
# US: "SYNQ_API_ENDPOINT": "https://api.us.synq.io"; AU: "https://api.au.synq.io"
},
# ... other configuration
)
```
For linking dbt models to Airflow tasks, see the [Airflow + dbt Core Linking guide](/orchestration-integrations/airflow-dbt-core).
### Dagster
1. **Configure environment:** Add `SYNQ_TOKEN=` to your `.env` file. For US region workspaces also add `SYNQ_API_ENDPOINT=https://api.us.synq.io`, and for AU `https://api.au.synq.io`.
2. **Update resources in `definitions.py`:**
```python theme={null}
resources = {
"dbt": DbtCliResource(
dbt_executable='synq-dbt',
project_dir=os.fspath(dbt_project_dir)
),
}
```
3. **Update assets in `assets.py`:**
```python theme={null}
@dbt_assets(manifest=dbt_manifest_path)
def jaffle_shop_dbt_assets(context: AssetExecutionContext, dbt: DbtCliResource):
dbt_target_path = Path('target')
yield from dbt.cli(["build"], target_path=dbt_target_path, context=context).stream()
```
### Docker
Add to your Dockerfile:
```dockerfile theme={null}
RUN wget -O /usr/bin/synq-dbt https://github.com/getsynq/synq-dbt/releases/latest/download/synq-dbt-amd64-linux && \
chmod +x /usr/bin/synq-dbt
```
Swap `latest/download` for `download/v2.1.0` if you would rather the image pin a
version.
### Linux/macOS
**Download and install:**
```shell theme={null}
# For Linux
wget -O ./synq-dbt https://github.com/getsynq/synq-dbt/releases/latest/download/synq-dbt-amd64-linux
# For macOS
wget -O ./synq-dbt https://github.com/getsynq/synq-dbt/releases/latest/download/synq-dbt-arm64-darwin
# Make executable and move to PATH
chmod +x ./synq-dbt && mv synq-dbt /usr/local/bin/synq-dbt
# Set your token and test
export SYNQ_TOKEN=
synq-dbt --version
```
# Use synq-dbt
## Basic usage
Replace your existing dbt Core commands with `synq-dbt`:
```shell theme={null}
# Instead of: dbt run --select finance --threads 5
synq-dbt run --select finance --threads 5
# Instead of: dbt test --select reports
synq-dbt test --select reports
# Instead of: dbt build
synq-dbt build
```
All dbt Core arguments and options work exactly the same way.
## Send catalog.json
None of the commands above write `catalog.json`. Add a separate `docs generate` step to the same job or DAG, after `run` or `build`:
```shell theme={null}
synq-dbt build
synq-dbt docs generate
```
`synq-dbt docs generate` runs `dbt docs generate`, which queries your warehouse and writes `catalog.json` to the target directory. `synq-dbt` then uploads it along with the other artifacts. With Airflow or Dagster, add `docs generate` as another `synq-dbt` command, the same way you run `build`.
If you run plain `dbt docs generate` instead, upload the catalog afterwards from the same working directory with `synq-dbt synq_upload_artifacts`.
If an upload doesn't include `catalog.json`, Coalesce Quality reuses the last catalog it received, but only if it is at most 7 days older than the upload's `manifest.json`. If there is no such catalog, the models are published without column types and descriptions, and the upload gets a recommendation to run `dbt docs generate`. After more than 30 days without a new catalog, the column information from the last one is no longer treated as current. It is replaced wherever the warehouse describes the same table.
If you deliberately don't send `catalog.json`, turn on **Skip catalog.json warnings** in the integration's advanced settings. This only hides the recommendation. The 7-day and 30-day rules still apply.
## Upload existing artifacts
If you have already generated dbt artifacts and want to upload them to Coalesce Quality:
```shell theme={null}
export SYNQ_TOKEN=
synq-dbt synq_upload_artifacts
```
**Include dbt logs:**
```shell theme={null}
dbt build | tee dbt.log
synq-dbt synq_upload_artifacts --dbt-log-file dbt.log
```
## Configuration options
**Environment variables:**
* `SYNQ_TOKEN` — Your integration token (required)
* `SYNQ_TARGET_DIR` — Artifact directory path (default: `target/`)
* `SYNQ_API_ENDPOINT` — API endpoint for your region (required outside EU)
**Regional configuration:**
EU region customers (default) don't need to set `SYNQ_API_ENDPOINT`. US and AU region customers must configure it.
For **US region** workspaces, set the API endpoint:
```shell theme={null}
export SYNQ_API_ENDPOINT=https://api.us.synq.io
```
For **EU region** workspaces (default), no additional configuration needed — the tool automatically uses `https://developer.synq.io`.
**Network requirements:**
* Allow outbound HTTPS traffic to `developer.synq.io:443` (EU), `api.us.synq.io:443` (US) or `api.au.synq.io:443` (AU)
**Data visibility:**
* Artifacts appear in Coalesce Quality within minutes of upload under normal conditions
* Failed uploads are logged and can be retried
* Typical payload sizes range from several megabytes to tens of megabytes depending on project size
For advanced configuration options and troubleshooting, see the [synq-dbt GitHub repository](https://github.com/getsynq/synq-dbt).
# Coalesce Quality options for dbt
Source: https://docs.synq.io/dbt-integrations/synq-options
Customize how dbt resources appear in Coalesce Quality using meta configuration
This guide explains how to use dbt's `meta` configuration to customize how your dbt resources are displayed and categorized in Coalesce Quality.
**Prerequisites:**
* A working dbt Core or dbt Cloud integration with Coalesce Quality
* Familiarity with dbt's [meta configuration](https://docs.getdbt.com/reference/resource-configs/meta)
# Overview
Coalesce Quality reads custom metadata from your dbt project's `meta` configuration to enhance how resources are displayed and organized. By adding specific options to your dbt models and tests, you can:
* Categorize tests into specific Check categories
* Improve organization and filtering in the Coalesce Quality interface
# Check category
Use the `check_category` option to assign a dbt test to a specific Check category in Coalesce Quality. This is useful for organizing standalone dbt tests (generic or singular) into meaningful groups.
## Configuration
Add the `quality` configuration to your test's `meta` block:
```sql theme={null}
{{ config(
meta = {
'quality': {'check_category': 'e2e'}
}
)
}}
```
The original `synq` namespace remains supported permanently, so existing
`meta: {synq: {check_category: ...}}` configurations do not need to change.
Use `quality` for new configurations.
## Example: Singular test
For a singular test file (e.g., `tests/e2e_order_flow.sql`):
```sql theme={null}
{{ config(
meta = {
'quality': {'check_category': 'e2e'}
}
)
}}
-- Test that verifies the complete order flow from creation to fulfillment
SELECT order_id
FROM {{ ref('orders') }}
WHERE status = 'created'
AND created_at < CURRENT_TIMESTAMP - INTERVAL '24 hours'
AND fulfilled_at IS NULL
```
## Example: Generic test in schema.yml
For generic tests defined in your schema files:
```yaml theme={null}
models:
- name: orders
columns:
- name: order_id
tests:
- unique:
meta:
quality:
check_category: 'data-quality'
- not_null:
meta:
quality:
check_category: 'data-quality'
```
## Example: Custom generic test
For custom generic tests, add the config at the top of the test file:
```sql theme={null}
{% test custom_validation(model, column_name) %}
{{ config(
meta = {
'quality': {'check_category': 'validation'}
}
)
}}
SELECT {{ column_name }}
FROM {{ model }}
WHERE {{ column_name }} IS NOT NULL
AND NOT {{ column_name }} REGEXP '^[A-Z]{2}[0-9]{6}$'
{% endtest %}
```
# How it works
Coalesce Quality automatically categorizes dbt tests based on the test type and package (e.g., `dbt`, `dbt_expectations`, `dbt_utils`). However, for singular tests or custom generic tests, it may not be able to determine the appropriate category automatically.
Using `check_category` allows you to:
* Override the automatic categorization for any test
* Assign meaningful categories to singular tests that would otherwise be uncategorized
* Group related tests together for better filtering and analysis in Coalesce Quality
# Custom categories
You can use any category name that makes sense for your organization. Common examples:
| Category | Description |
| - | - |
| `e2e` | End-to-end tests that validate complete workflows |
| `data-quality` | Tests for data completeness, accuracy, and consistency |
| `business-rules` | Tests that enforce business logic and constraints |
| `reconciliation` | Tests that compare data across sources |
| `validation` | Input validation and format checks |
| `sla` | Service level agreement compliance tests |
# Default categories
Coalesce Quality automatically assigns the following categories to known test types from `dbt`, `dbt_expectations`, `dbt_utils`, and other packages:
| Category | Description |
| - | - |
| `accepted_values` | Tests that validate values are within an allowed set |
| `nullness` | Tests for null or not-null constraints |
| `unique` | Uniqueness validation tests |
| `relationships` | Referential integrity and foreign key tests |
| `freshness` | Data timeliness and recency tests |
| `row_count` | Row count validation and volume tests |
| `range_validation` | Tests that check values are within expected ranges |
| `comparison` | Tests comparing values between columns or tables |
| `structure` | Schema structure and column existence tests |
| `match_regex` | Pattern matching and regex validation tests |
Setting `check_category` will override the default categorization for any test.
# Best practices
1. **Use custom categories** to group tests by business domain or testing strategy
2. **Be consistent** with category names across your project
3. **Document your categories** so team members understand what each category represents
# DWH Agent
Source: https://docs.synq.io/dw-integrations/agent
Integrating to databases via on-premise installed Coalesce Quality agent
This guide explains how to configure the Coalesce Quality Data Warehouse agent installed on-premise.
When possible, use standard integration methods as they are much simpler and provide full functionality.
Reach out to us for installation details.
## Data we collect
For the automated data anomaly testing, we collect the following:
* Number of rows in every table in the monitored dataset(s)
* Timestamp of the last change of data in all tables in the monitored dataset(s)
**We provide built-in monitors for data volume and freshness without accessing your actual data. However, for custom monitors, the agent needs access to query your raw data. Only the aggregated results are sent to the platform.**
# Setup DWH Agent integration
1. In the app, navigate to Data Sources and click "Add integration"
2. Select "On-premise DWH Agent" from the list of available integrations
3. Enter a title for your integration (e.g., "Production DWH Agent")
4. Click "Create". You will receive:
* `client_id`
* `client_secret`
Save these credentials - you'll need them to configure the agent in the next section.
Keep your client credentials secure. They allow the agent to authenticate with
the platform.
# Agent Configuration
Agent is configured via `agent.yaml` file or through environment variables.
## Example configuration:
```yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-dwh/v1/config.schema.json
synq:
client_id: "xxxx-xxxx-xxxx-xxx"
client_secret: "${SYNQ_CLIENT_SECRET}"
connections:
"pg-local":
name: "PG Local"
postgres:
host: 1.2.3.4
port: 54320
username: postgres
password: ${PG_PASSWORD}
database: db
allow_insecure: true
"bq-nifty-motif-341212":
bigquery:
project_id: big-query-project-id
region: europe-west2
"ch-staging":
name: Clickhouse
clickhouse:
host: host.clickhouse.cloud
port: 9440
instance_name: staging
username: default
password: ${STAGING_CLICKHOUSE_PASSWORD}
"prod-snowflake":
disabled: true
snowflake:
username: "SYNQ"
password: "${SNOWFLAKE_DWH_PASSSWORD}"
account: "snowflake-account"
warehouse: "SYNQ_WH"
databases: ["DB"]
role: "SYNQ_ROLE"
use_get_ddl: true
```
For details about all available configuration options, refer to the [JSON Schema](https://schemas.synq.io/synq-dwh/v1/config.schema.json) or the [HTML documentation](https://schemas.synq.io/synq-dwh/v1/config.html).
An `mssql` connection can sign in as the agent's own Windows or Kerberos identity instead of a SQL Server login. See [SQL Server: Windows Authentication](/transactional-databases/mssql#windows-authentication).
## ClickHouse: naming the service and choosing a database
ClickHouse has no container above a database, so something has to name the service itself. `instance_name` is that name, and `database` is a different setting entirely — the two are a key apart and are easy to swap.
```yaml theme={null}
connections:
"ch-prod":
clickhouse:
host: abc123xyz.europe-west4.gcp.clickhouse.cloud
port: 9440
instance_name: prod
database: analytics
username: quality
password: ${CLICKHOUSE_PASSWORD}
```
`instance_name` is the name this ClickHouse is published under, and the top element of every asset path and breadcrumb its tables appear in — `prod`, `staging`, `eu-analytics`. Leave it out and the host is used instead, which is correct but unreadable for a ClickHouse Cloud endpoint.
Two things follow from it being an identity rather than a label:
* **Give the same service the same name everywhere.** It is the agent-side equivalent of **Instance name** on a Coalesce Quality-hosted ClickHouse integration, so a warehouse that moves between the two keeps its assets only if the name matches.
* **Pick one and keep it.** Changing it republishes every table under new paths, and the old ones stop being produced.
`database` is the database the connection opens with, so an unqualified table name in a SQL test resolves against it. It does not restrict what is scraped: metadata comes from system tables and covers every database the user can see, whatever this says. Omit it to open on `default`. It matches **Default database** on a Coalesce Quality-hosted integration.
## ClickHouse: cluster reads and server settings
The examples above are ClickHouse Cloud services, which need neither of the keys below. A self-managed install usually needs `cluster`.
```yaml theme={null}
connections:
"ch-prod":
clickhouse:
host: clickhouse.internal
port: 9440
username: quality
password: ${CLICKHOUSE_PASSWORD}
cluster:
mode: all_replicas # or single_node
name: analytics_cluster # only applies to all_replicas
settings:
max_execution_time: "300"
```
`cluster` decides how the agent reads ClickHouse system tables. They are per-node, so on a service with more than one replica a plain read reflects whichever replica answered rather than the whole warehouse. See [How metadata is read](/dw-integrations/clickhouse#how-metadata-is-read) for the full explanation; in the agent config:
* **Omit `cluster`** to read across a cluster named `default`. Every ClickHouse Cloud service provides one.
* **`mode: all_replicas`** with a `name` reads across the cluster you name. List what your server has with `SELECT DISTINCT cluster FROM system.clusters`. This read needs `GRANT REMOTE ON *.*`.
* **`mode: single_node`** reads system tables directly on the connected node. This is the only setting that works on an install whose `remote_servers` defines no cluster, and it needs no `REMOTE` grant. Choose it only when the server really is one node: on a service with replicas it reports the metadata of whichever node answered, which looks like a successful but incomplete scrape. `name` does not apply.
`settings` are [ClickHouse settings](https://clickhouse.com/docs/operations/settings/settings) applied to every connection the agent opens, written as they would be in a connection string. `true` and `false` become `1` and `0`, whole numbers become integers, anything else is passed through as text. A name given here replaces the value the agent would otherwise use, which is how you raise a default that is too low for your warehouse — `max_execution_time` is the common one.
Both are the agent-side equivalents of the **Metadata reads** / **Cluster** and **Additional parameters** settings on a Coalesce Quality-hosted ClickHouse integration.
# BigQuery
Source: https://docs.synq.io/dw-integrations/bigquery
Integrating BigQuery with Coalesce Quality
This guide explains how to connect Coalesce Quality to BigQuery securely.
We need this information so we can collect relevant data about your tables.
To be able to finish this guide, you’ll need the following:
→ Access to modify your BigQuery configuration
⏱️ Estimated time to finish: 10 minutes.
## Data we collect
For the automated data anomaly testing, we collect the following:
* Number of rows in every table in the monitored dataset(s)
* Timestamp of the last change of data in all tables in the monitored dataset(s)
**To provide out-of-the-box monitors for volume and freshness, we don’t require access to your actual data. For custom monitors, however, access to query your raw data is required.**
Permission overview:
| Permission | Description | Included in BQ role | Purpose |
| - | - | - | - |
| bigquery.datasets.get | Get metadata about a dataset. | Data Viewer | automated / custom monitors |
| bigquery.datasets.getIamPolicy | Required by the Cloud Console to give the user the option of getting a dataset's IAM permissions. Fails open. The ability to actually perform the operation of getting the permissions is gated by the bigquery.datasets.get permission. | Data Viewer | automated / custom monitors |
| bigquery.jobs.create | Run jobs (including queries) within the project. | Job User | automated / custom monitors |
| bigquery.jobs.get | Get data and metadata on any job — required to retrieve the result of the metadata queries we submit. | Job User | automated / custom monitors |
| bigquery.jobs.list | List all jobs and retrieve metadata on any job submitted by any user. For jobs submitted by other users, details and metadata are redacted. | Resource Viewer | query logs |
| bigquery.jobs.listAll | List all jobs and retrieve metadata on any job submitted by any user. | Resource Viewer | query logs |
| bigquery.tables.get | Get table metadata. | Data Viewer | automated monitors |
| bigquery.tables.getData | Get table data. | Data Viewer | custom monitors (optional — skip for metadata-only) |
| bigquery.tables.list | List tables and metadata on tables. | Data Viewer | automated / custom monitors |
| bigquery.routines.get | Get metadata for user-defined functions, table-valued functions and stored procedures. | Data Viewer | connection validation only |
| bigquery.routines.list | List routines in a dataset. | Data Viewer | connection validation only |
| resourcemanager.projects.get | | Data/Jobs/Resource Viewer | All |
| resourcemanager.projects.getIamPolicy | Read the project's IAM policy: the users and service accounts bound to the project, with their roles. | Security Reviewer | platform users |
| iam.serviceAccounts.list | List the project's service accounts, with their display name, description and whether they are disabled. | Security Reviewer | platform users |
# Setup BigQuery access
### Create a dedicated Coalesce Quality role
1. Select the project with your BigQuery instance in the project picker.
2. Go to `IAM and Admin` → `Roles` → `Create Role`.
3. Fill in:
* Title: `Coalesce Quality Monitoring`
* Description: `Coalesce Quality Monitoring role`
* Role launch stage: `General Availability`
4. Click `Add permissions` and add:
```yaml theme={null}
bigquery.datasets.get
bigquery.datasets.getIamPolicy
bigquery.jobs.create
bigquery.jobs.get
bigquery.jobs.list
bigquery.jobs.listAll
bigquery.tables.get
bigquery.tables.getData
bigquery.tables.list
bigquery.routines.get
bigquery.routines.list
resourcemanager.projects.get
resourcemanager.projects.getIamPolicy
iam.serviceAccounts.list
```
5. Click `Create`.
### Create a service account
1. Go to `IAM and Admin` → `Service Accounts` → `Create Service Account`.
2. Fill in:
* Service account name: `synq-monitoring`
* Service account description: `Coalesce Quality Monitoring Service Account`
3. Click `Create and continue`.
4. In section *Grant this service account access to the project*, select the previously created `Coalesce Quality Monitoring` role, then click `Done`.
### Create a service account key
1. Open your newly created service account and switch to the `Keys` tab.
2. Click `Add key` → `Create new key`, choose **JSON**, and click `Create`.
3. Store the downloaded JSON key file securely.
### Platform users
`resourcemanager.projects.getIamPolicy` and `iam.serviceAccounts.list` let Coalesce Quality list who can query the project, so it can tell people from service accounts among the logins in your query history. They read IAM metadata only, never data. Both calls go through Google APIs that have to be enabled in the project the service account belongs to: **Cloud Resource Manager API** and **Identity and Access Management (IAM) API** (`APIs & Services` → `Library`).
Without them everything else works, and the integration shows a recommendation naming what is missing.
## Personal access (user OAuth)
In addition to the workspace service account above, you can let each Coalesce Quality member authenticate as themselves against BigQuery. Queries then run under the member's own Google identity and respect the IAM grants on that user. See [Data Warehouse Access](/security/dwh-access) for the full model and when to enable it.
To enable personal access, a Google Cloud admin creates a single OAuth 2.0 Client ID once, then pastes the client ID and secret into the BigQuery integration's edit page in Coalesce Quality.
### Configure branding
Skip this step if your project's Google Auth Platform is already configured.
1. In the Google Cloud console for the project that will host the OAuth app, go to `Google Auth Platform` → `Branding`.
2. Fill in the app name (e.g. `Coalesce Quality`), user support email, and developer contact.
3. Save.
### Choose the audience
1. Go to `Google Auth Platform` → `Audience`.
2. **User type**: pick `Internal` if every Coalesce Quality member has a Google account in your Workspace organisation, otherwise `External`.
If you use `External` and stay in **Testing** mode, Google caps refresh tokens at 7 days — members will need to re-consent weekly. Promote the publishing status to **Production** (or use `Internal`) for long-lived access.
### Create the OAuth client
1. Go to `Google Auth Platform` → `Clients` → `Create client`.
2. **Application type**: `Web application`.
3. **Name**: e.g. `Coalesce Quality`.
4. Under **Authorised redirect URIs**, add the URI(s) for the Coalesce Quality region(s) your members use, with no trailing slash:
* EU: `https://app.synq.io/auth/dwh-oauth/callback`
* US: `https://app.us.synq.io/auth/dwh-oauth/callback`
* AU: `https://app.au.synq.io/auth/dwh-oauth/callback`
5. Click `Create`. Copy the **Client ID** and **Client secret**.
### Enable the BigQuery API
In the same project, go to `APIs & Services` → `Library`, search for **BigQuery API**, and click `Enable` (already enabled if you scrape BigQuery from this project).
### Paste credentials into Coalesce Quality
In Settings → Integrations → *(your BigQuery integration)* → Warehouse access → Personal credentials, paste the Client ID and Client secret. Members will then see this BigQuery integration listed under Settings → Warehouse access → My credentials and can connect their own Google account.
Coalesce Quality requests two OAuth scopes during member consent: `https://www.googleapis.com/auth/bigquery` (BigQuery access — required to run queries, which BigQuery executes as jobs) and `https://www.googleapis.com/auth/userinfo.email` (to label the connected account in the UI). Member queries are also bounded by each member's IAM grants — the OAuth scope is the outer limit, not the policy, so a member with read-only IAM permissions still can only read.
## Network Configuration
If your BigQuery project has firewall rules or VPC configurations that restrict access, you may need to whitelist our IP addresses. See [Coalesce Quality IP Whitelist](/security/ip) for the complete list of IP addresses by region.
Once you've successfully completed these steps, input the following data in the platform.
## Input data in the Coalesce Quality UI
### Integration name
For example `BigQuery`
### Project ID
Open the project picker at the top of the Google Cloud console and copy the **ID** column value for your project.
### Service account key
The content of the JSON file you created earlier in the guide
### Region
Location of your BigQuery instance (typically US or EU)
### Blacklisted datasets (optional)
Comma-separated list of dataset name patterns to exclude from ingestion. Use `*` to match any string, e.g. `dbt_pr_*`.
### Datasets (optional)
Comma-separated list of dataset names to scrape explicitly (e.g. `analytics, mart_core`). When set, only these datasets are queried and the service account does **not** need the project-level `bigquery.datasets.list` permission — this lets you grant the Coalesce Quality Monitoring role on individual datasets instead of the whole project.
Leave empty to auto-discover every visible dataset in the project.
### Audit table FQN (optional)
Fully-qualified table name (e.g. `my-project.analytics.quality_sql_test__audit`) where Coalesce Quality writes SQL test execution results. The integration creates the table automatically if it does not yet exist. Leave empty if you do not run SQL tests.
Using this field requires two extra permissions on the target dataset that are **not** in the default role above: `bigquery.tables.create` and `bigquery.tables.updateData`. Add them to the Coalesce Quality Monitoring role (or grant them just on the audit dataset) before enabling the feature.
### Fetch query logs (recommended)
Enables ingestion of query history from `INFORMATION_SCHEMA.JOBS`, which powers anomaly-monitor training, data-usage insights, unused-table detection, and cost signals. Expect a small additional query cost (typically a few dollars per month).
# ClickHouse
Source: https://docs.synq.io/dw-integrations/clickhouse
Integrating ClickHouse with Coalesce Quality
Coalesce Quality has deep ClickHouse support. In fact, our platform is built on ClickHouse. See the talk by our CEO: [Building a Unified Data Platform with ClickHouse](https://clickhouse.com/blog/building-a-unified-data-platform-with-clickhouse)
This guide will show you how to securely connect Coalesce Quality to your ClickHouse service.
We need this information so we collect metadata about your tables.
To be able to finish this guide, you'll need the following:
→ Access to create users and grant privileges in ClickHouse (on ClickHouse Cloud, the `default` user)
⏱️ Estimated time to finish: 10 minutes.
## Data we collect
We read metadata from your ClickHouse service using the dedicated user described below. Every read goes through `system.*` tables — we never scan your table data to build the catalog. Depending on which features you enable, we ingest:
* **Catalog** — databases, tables, views, materialized views, columns, data types, positions, and table/column comments.
* **SQL definitions** — the `CREATE TABLE` / `CREATE VIEW` / `CREATE MATERIALIZED VIEW` statement per object. Used for lineage and to detect references between objects.
* **Keys and indexes** — primary keys, sorting keys, partition keys, and data-skipping indexes (`minmax`, `set`, bloom filter, and so on), surfaced as table constraints.
* **Table metrics** — row counts and last-modified timestamps, derived from `system.tables` and `system.parts`, for anomaly detection.
* **Query logs** *(opt-in)* — `system.query_log` for performance, cost, usage, and lineage signals.
These map to a small, fixed set of system tables:
| What | System table |
| - | - |
| Databases | `system.databases` |
| Tables, views, engines, comments, keys, DDL | `system.tables` |
| Columns, types, comments | `system.columns` |
| Data-skipping indexes | `system.data_skipping_indices` |
| Row counts and last-modified | `system.tables`, `system.parts` |
| Query logs *(opt-in)* | `system.query_log` |
**Metadata vs. data access.** The grants below give Coalesce Quality access to your **schema and DDL**, never to row data. Data-reading queries are issued only when you grant `SELECT` on your own databases — needed for [Custom SQL monitors](/monitor-types/custom-sql-monitor), data previews, and column profiling — or when you enable SQL test execution against the audit table.
We connect on a schedule (typically every 30 minutes). The metadata queries read system tables only and are negligible against a normal workload.
## Requirements
### How metadata is read
ClickHouse system tables are per-node, so on a service with more than one replica a plain read reflects whichever replica answered rather than the whole warehouse. Coalesce Quality therefore reads them through `clusterAllReplicas(, …)`, and the **Metadata reads** setting on the integration decides which cluster:
* **ClickHouse Cloud** — nothing to do. Every service exposes a cluster named `default`, which is the setting's default.
* **Self-managed ClickHouse** — list what your server has with `SELECT DISTINCT cluster FROM system.clusters` and enter that name in the **Cluster** field.
* **A single node with no cluster defined** — choose **Read the connected node only**. System tables are then read directly, which is the correct read when there is nothing to fan out to, and it needs no `REMOTE` privilege. The **Cluster** field does not apply.
Choose single-node reads only when the server really is one node. On a service with replicas it reports the metadata of whichever node answered, which looks like a successful but incomplete scrape.
### Network access
For the complete list of IP addresses by region, see [Coalesce Quality IP Whitelist](/security/ip).
Coalesce Quality connects over ClickHouse's **native protocol**. Allow inbound connections from the Coalesce Quality IPs for your region on port **`9440`**, the native TLS port.
On ClickHouse Cloud this is **Settings → Security → IP access list** for the service. If the list is set to *Anywhere* no change is needed, but we recommend restricting it to our published addresses.
**Connect over TLS.** Coalesce Quality reaches your service from the public internet, across which credentials and metadata must never travel in the clear. Use port `9440` with **Use SSL** enabled.
The only case for a plaintext connection — port `9000`, **Use SSL** off — is one that never leaves a private network, such as a VPN or peered network between your infrastructure and ours. If that is your setup, talk to [support](/support/support) before turning TLS off. For everything else, including every ClickHouse Cloud service, TLS is not optional.
## Setup ClickHouse access
### Create a dedicated user
Run the statements in this section as an admin user. On ClickHouse Cloud, open your service's **SQL console** from the left menu — it runs as `default`, which has the privileges to create users and grant on `system.*`. Replace `` with a secret you define for the integration.
```sql theme={null}
CREATE USER coalesce_quality IDENTIFIED WITH sha256_password BY '';
```
### Grant metadata access
```sql theme={null}
-- Required when metadata is read across a cluster, which is the default.
-- Skip it if the integration is set to read the connected node only.
GRANT REMOTE ON *.* TO coalesce_quality;
-- Catalog, DDL, keys, indexes, metrics
GRANT SELECT ON system.databases TO coalesce_quality;
GRANT SELECT ON system.tables TO coalesce_quality;
GRANT SELECT ON system.columns TO coalesce_quality;
GRANT SELECT ON system.data_skipping_indices TO coalesce_quality;
GRANT SELECT ON system.parts TO coalesce_quality;
```
**Grant the whole set, even if a smaller one appears to work today.**
ClickHouse has historically let some `system.*` tables be read without an explicit grant, filtering rows by what the user could otherwise see. Newer server versions enforce an explicit grant on the table instead. A service that upgrades — which on ClickHouse Cloud happens automatically, without notice — will then start rejecting reads that worked the day before, with `Not enough privileges` naming one table at a time. Granting the full list up front avoids discovering this one table per upgrade. See [Troubleshooting](#troubleshooting).
### Grant query log access (optional)
Required only if you enable *Fetch query logs*:
```sql theme={null}
GRANT SELECT ON system.query_log TO coalesce_quality;
```
### Grant data access (optional)
Required if you want Coalesce Quality to read rows — [Custom SQL monitors](/monitor-types/custom-sql-monitor), catalog data previews, column profiling, and SQL test execution:
```sql theme={null}
GRANT SELECT ON .* TO coalesce_quality;
```
If you configure an audit table for SQL tests, the user also needs to create and write it:
```sql theme={null}
GRANT CREATE TABLE, INSERT ON . TO coalesce_quality;
```
### Verify
```sql theme={null}
SHOW GRANTS FOR coalesce_quality;
```
## Input data in the Coalesce Quality UI
Go to **Settings → Integrations → Add integration → ClickHouse**.
### Where to find these values on ClickHouse Cloud
1. Open your service in the [ClickHouse Cloud console](https://console.clickhouse.cloud) and click **Connect** in the left menu.
2. In the dialog, switch the protocol drop-down from **HTTPS** to **Native**. This matters: the dialog opens on HTTPS and shows port `8443`, which this integration cannot use. On **Native** it shows port `9440`.
3. Copy the **hostname** from the `clickhouse client --host …` example. It has the form `...clickhouse.cloud`, for example `abc123xyz.europe-west4.gcp.clickhouse.cloud`. Take the hostname only — not the `--host` flag, and not an `https://` prefix.
Two things the dialog will *not* give you:
* **The user.** It shows credentials for the built-in `default` user. Use the dedicated `coalesce_quality` user you created above instead, so this integration's access is scoped and revocable on its own.
* **The password.** ClickHouse Cloud shows a service password once, at creation. This integration uses the password you set in [Create a dedicated user](#create-a-dedicated-user), which you control.
If your service has private endpoints configured, the dialog's path selector changes the hostname it shows. Pick the path that Coalesce Quality actually reaches you on — for an internet-facing service, the public one.
### Integration name
For example `ClickHouse`.
### Instance name
A unique name for this instance, for example `prod` or `staging`. It becomes part of the asset paths for everything this integration ingests, so **it cannot be changed after the integration is saved** — changing it later means creating a new integration.
### Host
The hostname of your service, without a scheme and without a trailing slash — for example `abc123xyz.europe-west4.gcp.clickhouse.cloud`. On ClickHouse Cloud, take it from the **Connect** dialog as described above.
### Port
Pre-filled with `9440`, the native TLS port. That is the right value for every ClickHouse Cloud service and for any service reachable over the internet — leave it alone unless you are on a private network, where a plaintext native connection uses `9000` (see [Network access](#network-access)).
### Username and password
`coalesce_quality` and the password you set above.
### Use SSL
On by default, paired with port `9440`. Turning it off sends your credentials and metadata unencrypted, which is only ever acceptable on a connection that stays inside a private network — see [Network access](#network-access).
### Default database
The database to connect to when a table reference omits one — for example `default`.
### Audit table FQN (optional)
Fully qualified name (`database.table`) of the table where Coalesce Quality should write SQL test audit logs, for example `default.quality_sql_test__audit`. Coalesce Quality creates the table if it does not exist. Leave empty if you only run metadata monitors and don't execute custom SQL tests against the warehouse.
### Metadata reads
Whether system tables are read **across a cluster** or **on the connected node only**. Choosing the cluster read reveals the **Cluster** field below it. See [How metadata is read](#how-metadata-is-read).
### Cluster
The cluster to read through, as named under `remote_servers` in your ClickHouse configuration. Defaults to `default`, which every ClickHouse Cloud service provides; `SELECT DISTINCT cluster FROM system.clusters` lists what a self-managed server has. Only shown when **Metadata reads** is set to read across a cluster.
### Additional parameters (optional)
[ClickHouse settings](https://clickhouse.com/docs/operations/settings/settings) applied to every connection this integration opens, as name/value pairs — for example `max_execution_time` `300` to give a large warehouse longer than the 60 seconds Coalesce Quality allows by default.
Values are typed the way ClickHouse types them in a connection string: `true` and `false` become `1` and `0`, whole numbers become integers, anything else is passed through as text. A name given here replaces the value Coalesce Quality would otherwise use, so this is also how you raise a default that is too low for your warehouse.
### Fetch query logs (Recommended)
Pulls from `system.query_log`. Required for proactive anomaly-monitor training, unused-table detection, usage insights, and cost insights. Needs the `system.query_log` grant above.
### Catalog scope and Metrics scope (optional)
Restrict which databases and tables are ingested into the catalog, and which of those are scanned for metrics. Leave both empty to ingest everything the user can see. `system`, `information_schema`, and `INFORMATION_SCHEMA` are always excluded.
## Personal credentials
In addition to the workspace credentials above, you can let each Coalesce Quality member authenticate as themselves against ClickHouse. Queries then run under the member's own ClickHouse user and respect the grants configured on it. See [Data Warehouse Access](/security/dwh-access) for the full model and when to enable it.
## Troubleshooting
### `Not enough privileges … it's necessary to have the grant SHOW COLUMNS ON system.columns`
The user is missing a grant on one of the system tables. The message names the table it stopped on, but there is usually more than one missing — fix them all at once by re-running the full [Grant metadata access](#grant-metadata-access) block, then re-running the same for [query logs](#grant-query-log-access-optional) if that option is enabled.
This most often appears **after a ClickHouse server upgrade** on a connection that had been working for months, because newer versions enforce explicit grants on system tables that were previously readable implicitly. Nothing changed on your side, and re-granting is the fix.
### `Requested cluster '…' not found. (CLUSTER_DOESNT_EXIST)`
Your ClickHouse has no cluster by that name. List what it does have with `SELECT DISTINCT cluster FROM system.clusters` and set **Metadata reads** accordingly; if the result is empty, choose **Read the connected node only**. See [How metadata is read](#how-metadata-is-read).
### `Not enough privileges … it's necessary to have the grant REMOTE ON *.*`
Reading across a cluster needs `GRANT REMOTE ON *.*`. Either grant it, per [Grant metadata access](#grant-metadata-access), or set **Metadata reads** to **Read the connected node only** if the server is a single node.
### `All connection tries failed … Connection refused`
Reported when replicas behind the service are unreachable — commonly a service that has scaled down to zero, or is mid-restart. It usually clears on the next scrape. If it persists, check the service is running and that the [IP access list](#network-access) allows our addresses.
### `Connect failed`
Check the host has no `https://` prefix and no trailing slash, that the port is `9440` with **Use SSL** enabled, and that the credentials are correct.
If you changed the port, check it is a native-protocol one. `8123` and `8443` are ClickHouse's HTTP ports and this integration speaks the native protocol, so they cannot connect — use `9440` with TLS, or `9000` without. A port that disagrees with the **Use SSL** setting fails outright rather than falling back, which surfaces the same way bad credentials do.
In some cases, permission grants can take a while to propagate. If you keep getting a `Connect failed` error after double-checking your details, try again after a minute.
# Databricks
Source: https://docs.synq.io/dw-integrations/databricks
Integrating Databricks with Coalesce Quality
This guide details the steps to integrate Coalesce Quality with Databricks using the Unity Catalog feature. This integration enables efficient data observability and management across your Databricks environment.
Before proceeding, ensure you have:
* Administrative access to your Databricks workspace
* Permissions to manage Unity Catalog settings
⏱️ Estimated time to complete: 10 minutes.
## Integration Overview
Integrating with Databricks through Unity Catalog allows you to monitor and manage data reliability and quality directly within your Databricks environment.
## Prerequisites
* **Databricks Workspace URL**: The URL of your Databricks workspace where Unity Catalog is configured.
* **OAuth Client ID and Client Secret** / **Access Token**: A Databricks credentials with permissions to access to monitored catalogs.
* **Warehouse ID**: The identifier for the SQL warehouse within Databricks that will be used to run monitoring queries. We recommend using Serverless SQL Warehouses.
## Step-by-Step Guide
### Step 1: Configuring Unity Catalog
1. **Log in to your Databricks workspace**:
Navigate to your Databricks workspace by entering the Workspace URL in your browser.
2. **Access the Unity Catalog**:
From the sidebar, select 'Data' and then 'Unity Catalog' to configure the data catalog settings.
3. **Create or Select a Catalog**:
Choose an existing catalog or create a new one for the integration to operate on.
### Step 2: Create authentication method
#### Option 1: Using Service Principal and OAuth (recommended)
1. **Create Service Principal**:
Navigate to the 'Admin Console' and select 'Identity and access' > 'Service Principals' > 'Manage'. Click on 'Add Service Principal' > 'Add new' and provide the name of the service principal. Note the generated Service Principal ID.
2. **Generate OAuth Secret**:
Click on the created Service Principal, go to the Secrets tab and click on 'Generate secret'. Note the generated OAuth token securely.
3. **Assign Permissions**:
Assign the necessary permissions to the Service Principal to access the Unity Catalog and other required resources.
```SQL theme={null}
GRANT USE CATALOG ON CATALOG TO ``;
GRANT USE SCHEMA ON CATALOG TO ``;
GRANT SELECT ON CATALOG TO ``;
```
These catalog grants are enough to read metadata and run monitoring queries, but they are **not** enough to build column- and table-level lineage. See [Query log lineage permissions](#query-log-lineage-permissions) below.
#### Option 2: Using Personal Access Token (not recommended)
1. **Navigate to the User Settings**:
Click on your profile at the bottom left corner and select 'User Settings'.
2. **Access Tokens**:
Go to the 'Access Tokens' tab and click on 'Generate New Token'.
Enter a description, set the expiration according to your policy, and note the generated token securely.
### Step 3: Configure the Integration
1. **Log in to Coalesce Quality**:
Access your dashboard.
2. **Add Databricks as an Integration**:
Navigate to 'Data Sources', select 'Add integration', and choose 'Databricks'.
3. **Enter Integration Details**:
Provide the Workspace URL, Access Token, and Warehouse ID to establish the connection.
4. **Network Configuration**:
If your Databricks workspace has network access controls or firewall restrictions, you may need to whitelist our IP addresses. See [Coalesce Quality IP Whitelist](/security/ip) for the complete list of IP addresses by region.
5. **Set Exclusion Rules**:
Define any exclusion rules for catalogs, schemas, and tables to tailor the monitoring to your needs.
## Query log lineage permissions
Coalesce Quality derives cross-table lineage from your Databricks **query history**: it reads the executed statements (such as `CREATE TABLE AS SELECT` and `MERGE`) and parses the relationships between source and target tables. Views are the exception — they carry their own SQL definition, so they show lineage without query history.
Databricks controls two things separately: which queries the service principal can see, and whether it can read their text. Lineage needs both, so the catalog grants above are not sufficient on their own.
### See the queries
A service principal (or user) sees:
* its **own** queries; plus
* queries that ran on a SQL warehouse it has at least **CAN VIEW** access to.
A workspace admin sees all queries. Without warehouse access, the integration sees only the queries it runs itself, so the statements that build your tables stay invisible.
Grant the service principal access to the query history of the warehouse(s) where your transformations run. Choose one of:
* Grant **CAN VIEW** (or **CAN MANAGE**) on each SQL warehouse that executes your transformations, via the warehouse's **Permissions** settings, **or**
* Make the service principal a **workspace admin**.
### Read the query text
Databricks masks the text of every query as `` unless the reader is an **account admin** or a member of the account-level group **`databricks_pii_access`**. Workspace admin and warehouse permissions do not lift the mask.
The group does not exist until an account admin creates it:
1. Sign in to the Databricks Account Console as an account admin.
2. Open **User management** → **Groups** → **Add group** and name it exactly `databricks_pii_access` (the name is case-sensitive).
3. Add the integration's service principal to the group as a member.
Queries whose text is masked still appear in Coalesce Quality with their user, warehouse, duration and metrics, but they are not attributed to the tables they read or wrote, and **no lineage is produced** from them. The integration's health page raises a recommendation with the number of masked queries in each fetch until the service principal can read their text.
Query history only covers queries executed on **SQL warehouses**. Statements that run on all-purpose clusters, Jobs compute, or Delta Live Tables / Lakeflow pipelines do not appear in query history regardless of permissions — so make sure the transformations you want lineage for run on a SQL warehouse.
## Personal access (user OAuth)
In addition to the workspace service principal above, you can let each Coalesce Quality member authenticate as themselves against Databricks. Queries then run under the member's own Databricks user and respect the Unity Catalog grants on that user. See [Data Warehouse Access](/security/dwh-access) for the full model and when to enable it.
To enable personal access, a Databricks **account admin** registers a custom OAuth app once at the account level, then pastes the client ID and secret into the Databricks integration's edit page in Coalesce Quality. The same app can be reused across every workspace in the account.
### Register the OAuth app in Databricks
1. Sign in to the [Databricks Account Console](https://accounts.cloud.databricks.com) (`accounts.cloud.databricks.com` for AWS, `accounts.azuredatabricks.net` for Azure, `accounts.gcp.databricks.com` for GCP) as an account admin.
2. Open `Settings` → `App connections` → `Add connection`.
3. Fill in:
* **Name**: e.g. `Coalesce Quality`.
* **Redirect URLs** — add the URL(s) for the Coalesce Quality region(s) your members use, with no trailing slash:
* EU: `https://app.synq.io/auth/dwh-oauth/callback`
* US: `https://app.us.synq.io/auth/dwh-oauth/callback`
* AU: `https://app.au.synq.io/auth/dwh-oauth/callback`
* **Access scopes**: `all-apis` and `offline_access`.
* Mark the app as **Confidential** (Coalesce Quality stores the client secret server-side).
4. Save. Copy the **Client ID** and **Client secret** — the secret is shown only once.
If your Databricks tier exposes app connections at the workspace level as well (`/settings` → `Developer` or `Identity and access` → `App connections`), either surface works — the resulting OAuth app is the same. Account-level registration is the recommended path because the app then covers every workspace in the account.
Coalesce Quality builds the authorize URL automatically from the workspace URL you configured on the integration (`/oidc/v1/authorize`) — you don't need to enter the authorize endpoint anywhere.
### Grant Unity Catalog access to your members
Personal access uses each member's own Unity Catalog grants — the OAuth app itself does not need catalog grants. Make sure every member who will use this path has at least:
```sql theme={null}
GRANT USE CATALOG ON CATALOG TO ``;
GRANT USE SCHEMA ON CATALOG TO ``;
GRANT SELECT ON CATALOG TO ``;
```
Members also need access to the SQL warehouse configured on the integration — `CAN USE` on the warehouse is enough.
### Paste credentials into Coalesce Quality
In Settings → Integrations → *(your Databricks integration)* → Warehouse access → Personal credentials, paste the Client ID and Client secret. Members will then see this Databricks integration listed under Settings → Warehouse access → My credentials and can connect their own Databricks user.
## Conclusion
Once configured, the platform will begin monitoring the specified data assets within Databricks, leveraging Unity Catalog for enhanced data management and observability.
For further assistance, contact our support team.
# Microsoft Fabric
Source: https://docs.synq.io/dw-integrations/fabric
Integrating Microsoft Fabric with Coalesce Quality
This guide explains how to connect Coalesce Quality to a Microsoft Fabric workspace securely.
We need this information so we can collect metadata about the tables in your Fabric Warehouses and Lakehouses.
To finish this guide, you'll need the following:
→ Permission to register a Microsoft Entra application (service principal) in your tenant, or an admin who can
→ A **Fabric administrator** to enable the tenant setting that lets service principals call Fabric
→ A workspace **admin** to give the service principal a role on the workspace (**Contributor** for the full feature set, including query history)
⏱️ Estimated time to finish: 15 minutes.
Microsoft Fabric exposes each workspace through a shared **SQL analytics endpoint** that speaks T-SQL. Coalesce Quality connects to that endpoint to read the metadata of every Warehouse and Lakehouse in the workspace. A single integration covers the whole workspace — each Warehouse / Lakehouse appears as a database, and Coalesce Quality queries across them using three-part `[database].[schema].[table]` names.
Authentication is via a Microsoft Entra ID **service principal**. Fabric does not accept SQL logins, so this is the only way to connect a headless integration.
## Data we collect
We read metadata from your workspace's SQL analytics endpoint using the dedicated service principal described below. Depending on which features you enable in the integration, we ingest:
* **Catalog** — the workspace's databases (Warehouses and Lakehouse SQL endpoints), schemas, tables, views, columns, data types, and constraints. Fabric constraints (primary key / unique / foreign key) are informational (`NOT ENFORCED`) but are still read from metadata.
* **SQL definitions** — the T-SQL of views, stored procedures, and functions. Used for lineage and to detect references between objects.
* **Table metrics** — row counts and last-modified timestamps for anomaly detection, collected for the tables in scope (see **Metrics scope** below).
* **Query history** *(recommended, on by default)* — read from the workspace's `queryinsights` system views for cost attribution and SQL / column-level lineage. Management-plane reads only — no per-query billing. Requires the service principal to have the **Contributor** (or higher) workspace role — see [Give the service principal a role on the workspace](#3-give-the-service-principal-a-role-on-the-workspace).
**Metadata only.** The service principal below only needs to read your **schema and DDL**, never row data. Coalesce Quality issues data-reading queries (row counts, samples for monitors) only when you enable the corresponding opt-in feature in the integration UI. With every opt-in disabled, the integration runs metadata queries only.
## Set up access
### 1. Register an Entra service principal
The integration authenticates as a Microsoft Entra application (service principal) with a client secret. You can create one in the [Entra admin center](https://entra.microsoft.com) (**App registrations → New registration**), or with the Azure CLI:
```bash theme={null}
# Sign in to your tenant
az login --tenant
# 1. Create the app registration
APP_ID=$(az ad app create \
--display-name "coalesce-quality-fabric-scraper" \
--sign-in-audience AzureADMyOrg \
--query appId -o tsv)
echo "Client (application) ID: $APP_ID"
# 2. Create the service principal for that app
az ad sp create --id "$APP_ID"
# 3. Mint a client secret (the value is shown only once — store it securely)
az ad app credential reset \
--id "$APP_ID" \
--display-name "coalesce-quality" \
--years 1 \
--query "{clientId:appId, clientSecret:password, tenantId:tenant}" -o json
```
Take note of the **Client ID**, **Client secret**, and **Tenant ID** — you'll enter them in the Coalesce Quality UI.
### 2. Allow service principals to use Fabric
A **Fabric administrator** must enable this once for the tenant:
1. Open the [Fabric admin portal](https://app.fabric.microsoft.com/admin-portal) → **Tenant settings → Developer settings**.
2. Enable **"Service principals can use Fabric APIs"**.
3. We recommend scoping it to a security group that contains only this service principal, to keep the blast radius small.
Without this setting the service principal cannot obtain a Fabric token and every connection fails.
### 3. Give the service principal a role on the workspace
A workspace admin adds the service principal to the workspace that holds the Warehouses / Lakehouses you want to monitor. Open the workspace → **Manage access → Add people or groups**, search for the app registration by name, and assign a role.
Which role you need depends on the features you enable:
| Feature | Minimum access |
| - | - |
| Databases, schemas, tables, columns, constraints | **Viewer** |
| View / procedure / function **SQL definitions** (lineage) | **Viewer** + `GRANT VIEW DEFINITION` (see step 4), or **Contributor** |
| **Table metrics** and monitor samples (row data) | **Viewer** + `GRANT SELECT` (see step 4), or **Contributor** |
| **Query history** (`queryinsights`) | **Contributor**, Member, or Admin |
We recommend **Contributor** — it covers every feature, including query history, with no extra SQL grants. Query history in particular reads Fabric's `queryinsights` system views, which are **only accessible to Contributor / Member / Admin**; a Viewer cannot read them even with SQL grants.
Choose **Viewer** only for tightest least privilege, and add the SQL grants in step 4 to recover SQL definitions and metrics. Query history is not available to a Viewer.
### 4. (Optional) Least-privilege SQL grants for a Viewer
If you assigned **Viewer** rather than Contributor, grant the service principal exactly what the enabled features read. Run this in each Warehouse you monitor, using the app registration's display name — Fabric resolves the Entra principal by name, so no separate user-creation step is needed:
```sql theme={null}
GRANT VIEW DEFINITION TO [coalesce-quality-fabric-scraper]; -- SQL definitions / lineage
GRANT SELECT TO [coalesce-quality-fabric-scraper]; -- only if table metrics / samples are enabled
```
These grants cannot unlock query history: `queryinsights` is gated by the workspace role, so leave the service principal on **Contributor** (or higher) if you want **Fetch query history** enabled.
### 5. Network access
The Fabric SQL analytics endpoint is a public Azure endpoint (`.datawarehouse.fabric.microsoft.com`, port 1433, TLS required). There is nothing to allowlist on the Fabric side. See the [Coalesce Quality IP Whitelist](/security/ip) only if your tenant restricts outbound access to the endpoint by IP.
## Input data in the Coalesce Quality UI
Navigate to **Data Sources → Add integration → Microsoft Fabric** and provide:
### Integration name
For example `Microsoft Fabric`.
### SQL analytics endpoint
The workspace SQL analytics endpoint, e.g. `.datawarehouse.fabric.microsoft.com`. You'll find it on any Warehouse or SQL analytics endpoint in the workspace under **Settings → SQL analytics endpoint** (the SQL connection string). Every Warehouse and Lakehouse in the workspace shares this host, so one integration covers the whole workspace.
### Default warehouse (optional)
The Warehouse or Lakehouse name used to resolve *unqualified* ad-hoc queries; leave empty to use `master`. Metadata and monitor SQL are fully qualified across every Warehouse in the workspace, so this only affects ad-hoc queries.
### Microsoft Entra service principal
* **Application (client) ID** — the Entra application (client) ID from step 1.
* **Client secret** — the service principal client secret. Stored encrypted and never displayed again in our UI.
* **Directory (tenant) ID (optional)** — only needed when the service principal lives in a different Entra tenant than the Fabric endpoint; inferred automatically when left blank.
### Data collection
* **Fetch query history** *(recommended, on by default)* — reads the workspace's `queryinsights` query history for cost attribution and SQL / column-level lineage. Management-plane reads only — no per-query billing. Needs the service principal on the **Contributor** (or higher) workspace role; a Viewer cannot read `queryinsights`. Databases without a `queryinsights` schema (Lakehouse SQL endpoints, Fabric SQL databases, mirrored databases) are skipped automatically.
### Scope
Narrow the workspace down to what you want to monitor. Both scopes support include **and** exclude rules at the **Warehouse**, **schema**, and **table** levels:
* **Catalog scope** — which Warehouses, schemas, and tables are ingested into the catalog. Empty means everything is ingested.
* **Metrics scope** — which tables are scanned for metrics. Empty means metrics are collected for all ingested tables.
## Personal access (user OAuth)
In addition to the workspace service principal above, you can let each Coalesce Quality member authenticate as themselves against Fabric through Microsoft Entra. Queries then run under the member's own Entra identity and respect the Fabric workspace roles, item permissions, and row-level security on that user. See [Data Warehouse Access](/security/dwh-access) for the full model and when to enable it.
To enable personal access, an Entra admin registers a **separate** OAuth app once (distinct from the connection service principal above), then pastes its client ID and secret into the Fabric integration's edit page in Coalesce Quality under **Warehouse access**.
### Register the OAuth app in Microsoft Entra
1. In the [Entra admin center](https://entra.microsoft.com), open **App registrations → New registration** and give it a name, e.g. `coalesce-quality-fabric-user-oauth`.
2. Under **Authentication → Add a platform → Web**, add the **Redirect URI(s)** for the Coalesce Quality region(s) your members use, with no trailing slash:
* EU: `https://app.synq.io/auth/dwh-oauth/callback`
* US: `https://app.us.synq.io/auth/dwh-oauth/callback`
* AU: `https://app.au.synq.io/auth/dwh-oauth/callback`
3. Under **API permissions → Add a permission → APIs my organization uses**, search for **Azure SQL Database**, choose **Delegated permissions**, and add **`user_impersonation`** (this is the `https://database.windows.net/user_impersonation` scope). No tenant-admin consent is required — it's user-consentable, so each member consents once at sign-in.
4. Under **Certificates & secrets → New client secret**, create a secret and copy its value (shown only once).
5. Copy the **Application (client) ID** and the **client secret**.
In Settings → Integrations → *(your Fabric integration)* → Warehouse access → Personal credentials, paste the Client ID and Client secret. Members will then see this Fabric integration listed under Settings → Warehouse access → My credentials and can connect with their own Entra identity.
Coalesce Quality builds the Entra authorize URL automatically (`https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize`) — you don't need to enter an authority or tenant. `organizations` lets each member's home tenant resolve the app, so one app registration works across every workspace and Coalesce Quality region whose redirect URI you added in step 2.
# MotherDuck
Source: https://docs.synq.io/dw-integrations/motherduck
Integrating MotherDuck with Coalesce Quality
This guide explains how to connect Coalesce Quality to your MotherDuck account securely.
We need this information so we can collect relevant metadata about your DuckDB tables for monitoring and data quality testing.
To be able to finish this guide, you’ll need the following:
→ Access to your MotherDuck workspace and API token
⏱️ Estimated time to finish: 5 minutes.
## Data we collect
For automated data anomaly testing, we collect the following from your MotherDuck instance:
* Table names and metadata
* Number of rows in each table
* Timestamp of the last data update
**We don’t access or store your raw data — only metadata needed to assess freshness, volume, and stability.**
## Setup MotherDuck Access
### 1. Get your MotherDuck account name
1. Go to [app.motherduck.com](https://app.motherduck.com) and log in.
2. Click your **profile icon** in the top-right corner.
3. In the dropdown, you’ll see your **organization name** and **email**.\
Your account name is typically your **organization name** (e.g. `acme-corp`) and often matches the prefix of your default database — for example, `acme-corp.db`.
You’ll need this account name when filling in the **MotherDuck account** field in the platform.
### 2. Generate a MotherDuck API token
1. While logged in to [app.motherduck.com](https://app.motherduck.com), click your **profile icon** again and choose **Account settings**.
2. Navigate to the **API Tokens** tab.
3. Click **Generate new token**, give it a name (e.g. `synq-integration`), and choose an expiry period.
4. Copy the generated token immediately — you won’t be able to see it again once you leave the page.
5. Paste it into the **MotherDuck API token** field in the platform.
Alternatively, if you’re using the CLI, you can generate a token via:
```bash theme={null}
motherduck auth token
```
# Redshift
Source: https://docs.synq.io/dw-integrations/redshift
Integrating Redshift with Coalesce Quality
This guide will show you how to securely connect Coalesce Quality to your Redshift cluster or Redshift Serverless workgroup.
We need this information so we collect metadata about your tables.
To be able to finish this guide, you'll need the following:
→ Access to modify your Redshift configuration as a superuser
⏱️ Estimated time to finish: 10 minutes.
## Data we collect
We read metadata from your Redshift database using the dedicated user described below. Depending on which features you enable in the integration, we ingest:
* **Catalog** — databases, schemas, tables, views, late-binding views, materialized views, columns, comments, data types, constraints (informational primary / unique / foreign keys).
* **SQL definitions** — view definitions, materialized view definitions, stored procedure bodies, and UDF bodies. Used for lineage and to detect references between objects.
* **Distribution and sort metadata** — `DISTSTYLE`, `DISTKEY`, `SORTKEY` (compound / interleaved / auto) per table.
* **Table metrics** *(opt-in)* — row counts and last-modified timestamps for anomaly detection.
* **Query logs** *(opt-in)* — `SYS_QUERY_HISTORY` for performance, cost, and lineage signals.
* **Freshness from query logs** *(opt-in)* — `SYS_QUERY_DETAIL` for tables touched by recent writes. Requires the `sys:monitor` role on Redshift Serverless.
* **External tables** — Spectrum external schemas and tables, when present.
**Metadata vs. data access.** The baseline grants below give Coalesce Quality access to your **schema and DDL**, never to row data. With every opt-in disabled, the integration runs metadata-only queries against `SVV_*` and `pg_catalog` views. Data-reading queries are issued only when you enable *Fetch table metrics*, *Fetch query logs*, *Freshness from query logs*, or run SQL test execution against the audit table.
We connect on a schedule (typically every 30 minutes). On Redshift Serverless this corresponds to roughly one minute of compute per scrape at the workgroup's base capacity — the actual bill depends on your base RPU setting. On provisioned Redshift our queries run on your existing cluster's compute and are negligible compared to a regular workload.
## Setup Redshift access
### Create a dedicated user with sufficient permissions
1. Open the Query Editor in Redshift and make sure the right cluster / workgroup and database are selected.
2. Run the following SQL as a superuser to create a dedicated user and group with the necessary read-only permissions, where `` is a secret you define for the integration.
```sql theme={null}
CREATE GROUP synq;
CREATE USER synq_user PASSWORD '' IN GROUP synq;
-- Catalog metadata views (svv_*)
GRANT SELECT ON pg_catalog.svv_redshift_databases TO GROUP synq;
GRANT SELECT ON pg_catalog.svv_all_schemas TO GROUP synq;
GRANT SELECT ON pg_catalog.svv_all_tables TO GROUP synq;
GRANT SELECT ON pg_catalog.svv_all_columns TO GROUP synq;
GRANT SELECT ON pg_catalog.svv_table_info TO GROUP synq;
GRANT SELECT ON pg_catalog.svv_external_tables TO GROUP synq;
```
3. Grant `USAGE` on each schema you want monitored. This is required even for views that use `WITH NO SCHEMA BINDING` — both the schema in which the late-binding view is defined and any schemas it reads from need `USAGE`.
```sql theme={null}
GRANT USAGE ON SCHEMA TO GROUP synq;
```
To grant `USAGE` on every schema in the database:
```sql theme={null}
DO $do$
DECLARE
sch text;
BEGIN
FOR sch IN SELECT nspname FROM pg_namespace
LOOP
EXECUTE format($$ GRANT USAGE ON SCHEMA %I TO GROUP synq $$, sch);
END LOOP;
END;
$do$;
```
4. Grant the user visibility into stored procedures, UDFs, and materialized views in the schemas you want monitored. Without these grants, procedure / UDF metadata and materialized view definitions will not be collected.
```sql theme={null}
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA TO GROUP synq;
GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA TO GROUP synq;
ALTER DEFAULT PRIVILEGES IN SCHEMA
GRANT EXECUTE ON FUNCTIONS TO GROUP synq;
ALTER DEFAULT PRIVILEGES IN SCHEMA
GRANT EXECUTE ON PROCEDURES TO GROUP synq;
```
5. (Optional) Grant access to table data within a schema. Required if you want Coalesce Quality to run monitors on your raw data — for example [Custom SQL monitors](/monitor-types/custom-sql-monitor):
```sql theme={null}
GRANT SELECT ON ALL TABLES IN SCHEMA TO GROUP synq;
ALTER DEFAULT PRIVILEGES IN SCHEMA
GRANT SELECT ON TABLES TO GROUP synq;
```
### Enable freshness monitoring (Redshift Serverless)
To estimate table freshness from query logs, the user needs access to `SYS_QUERY_DETAIL`. On Redshift Serverless this is gated behind the predefined `sys:monitor` role.
```sql theme={null}
GRANT ROLE sys:monitor TO synq_user;
```
For provisioned Redshift, equivalent visibility comes from `STV_*` / `STL_*` views — see the [official Redshift system table reference](https://docs.aws.amazon.com/redshift/latest/dg/cm_chap_system-tables.html).
### Whitelist the Coalesce Quality IP
For the complete list of IP addresses by region, see [Coalesce Quality IP Whitelist](/security/ip).
1. From Redshift, navigate to `Workgroup configuration` and click your default workgroup.
2. Under Network and security click `Edit` and check the *Turn on Publicly accessible* checkmark. Then click `Save changes`.
3. Close the window to return to the workgroup view and click your VPC security group.
4. Under the *Inbound rules* tab click `Edit inbound rules`, then `Add rule`:
* Type: `Redshift` (port 5439)
* Source: `Custom` with `34.105.135.39` (EU) or `35.238.250.82` (US)
5. Click `Save rules`.
### Database, dataset and host name
1. Take a note of your database name. It can be found in the Query Editor.
2. Go to `Workgroup configuration` and select your workgroup. Take a note of your `JDBC URL`.
## Input data in the Coalesce Quality UI
### Integration name
For example `Redshift`.
### Database
The name of your database (e.g. `analytics_prod`).
### Host
The hostname of your workgroup endpoint (e.g. `my-workgroup.123456789012.eu-central-1.redshift-serverless.amazonaws.com` or `my-cluster.us-east-1.redshift.amazonaws.com`).
To find the host: open Workgroup configuration, copy the Endpoint, and strip the trailing `:5439/` part.
### Port
Default is `5439`.
### User and password
`synq_user` and the password you set in step 2 above.
### Audit table name (optional)
Fully qualified name (`schema.table`) of the table where Coalesce Quality should write SQL test audit logs — for example `analytics.quality_sql_test__audit`. Coalesce Quality creates the table if it does not exist. Leave empty if you only run metadata monitors and don't execute custom SQL tests against the warehouse.
### SSH Tunnel (optional)
If your Redshift workgroup is private and only reachable through a bastion host, click `Add SSH tunnel` and fill in the user, hostname and port of the bastion (`22` unless you run SSH elsewhere). Leave this empty for clusters reachable via the public endpoint (the standard setup described above).
Coalesce Quality authenticates to the bastion with an RSA key pair it generates when you save the integration. Only the public half leaves the platform; it is shown under **SSH tunnel key pair** at the bottom of the integration's page. Add it as a line in the `~/.ssh/authorized_keys` file of the bastion user you entered. The bastion refuses the connection until that line is in place, so testing the connection before then fails.
#### Rotating the tunnel key pair
The same **SSH tunnel key pair** section replaces the key. This needs the Admin role.
**Generate a new key pair** takes effect immediately, and the key it replaces keeps being offered for a week, so the tunnel carries on working while you install the new one. The section shows the previous public key and the date it stops being offered.
Add the new public key to `authorized_keys`, then delete the line holding the previous one. Removing that line is what revokes the old key — Coalesce Quality has no access to your bastion, so a key left in the file stays usable after the week is over.
Only the most recent replacement is kept, so generating a second key pair while a previous key is still being offered retires that one straight away. Finish installing one key before generating the next.
### Data collection options
Two opt-in checkboxes control what beyond catalog metadata Coalesce Quality reads:
* **Fetch query logs (Recommended)** — pulls from `SYS_QUERY_HISTORY`. Required for proactive anomaly-monitor training, unused-table detection, and cost insights. Adds a small recurring query cost — typically a few dollars per month on Serverless.
* **Estimate freshness of tables from `SYS_QUERY_DETAIL` view** — derives a per-table last-modified timestamp from query history. Requires the `sys:monitor` role on Serverless (see [Enable freshness monitoring](#enable-freshness-monitoring-redshift-serverless) above).
In some cases, permission grants can take a while to propagate. If you keep getting a `Connect failed` error after double-checking your details, try again after a minute.
# Snowflake
Source: https://docs.synq.io/dw-integrations/snowflake
Integrating Snowflake with Coalesce Quality
This guide will show you how to securely connect Coalesce Quality to your Snowflake instance.
We need this information so we collect metadata about your tables.
To be able to finish this guide, you’ll need the following:
→ Access to modify your Snowflake configuration as account admin
⏱️ Estimated time to finish: 10 minutes.
## Data we collect
We read metadata from your Snowflake account using the dedicated role described below. Depending on which features you enable in the integration, we ingest:
* **Catalog** — databases, schemas, tables, views, materialized views, dynamic tables, semantic views, streams, comments, column types, tags.
* **Stages** *(opt-in grant)* — `SHOW STAGES` for each named stage the role can see: its type, URL, cloud, region and storage integration. With query logs enabled, the `COPY INTO` statements that load a table from a stage or unload one to it become lineage between the stage and the table.
* **Apps, notebooks, warehouses and pipes** *(opt-in)* — `SHOW STREAMLITS`, `SHOW NOTEBOOKS`, `SHOW PIPES`, `SHOW CORTEX SEARCH SERVICES` and `SHOW WAREHOUSES` for each object the role can see: a Streamlit app's title and warehouse, a notebook's warehouses, a pipe's `COPY` statement and the stage it loads from, a Cortex Search service's query and warehouse, and a warehouse's size, type and auto-suspend.
* **SQL definitions** — `GET_DDL` output for tables, views, materialized views, dynamic tables, semantic views, streams, stored procedures, and user-defined functions. Used for lineage and to detect references between objects.
* **Constraints** — primary keys (via `SHOW PRIMARY KEYS`) and clustering keys. Foreign keys, uniques, and checks are read from `GET_DDL` when present.
* **Table metrics** *(opt-in)* — row counts and last-modified timestamps for anomaly detection.
* **Query logs** *(opt-in)* — `SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY` for performance, cost, and lineage signals.
* **Tasks** *(opt-in)* — `SHOW TASKS` plus `TASK_HISTORY` for execution outcomes.
* **Stored procedures and UDFs** — `SHOW PROCEDURES` / `SHOW USER FUNCTIONS` plus `GET_DDL` for SQL, JavaScript, Python, Java, and Scala languages (automatic when the role has `USAGE` on them).
* **Table-level lineage** *(opt-in)* — via `SNOWFLAKE.CORE.GET_LINEAGE` on Enterprise+ editions.
**Metadata vs. data access.** The baseline grants below give Coalesce Quality access to your **schema and DDL**, never to row data. The `SELECT` privileges on tables/views/etc. are required so the role can call `SHOW` and `GET_DDL` (Snowflake gates object visibility behind any object privilege), but Coalesce Quality only issues data-reading queries when you enable the corresponding opt-in feature in the integration UI — *Fetch table metrics*, *Fetch query logs*, *Fetch table lineage*, or SQL test execution against the audit table. With every opt-in disabled, the integration runs metadata commands only.
We use a dedicated XSMALL warehouse to isolate our workload from your production compute, and a dedicated user and role with the minimum required privileges. Metadata is collected on a schedule (typically every 30 minutes).
*This should be expected to consume \$0.0003 ([see warehouses overview](https://docs.snowflake.com/en/user-guide/warehouses-overview)) x 48 (every 30 minutes) x 10 (10s per check) = 0.14 credits per day.*
Before you execute your scripts you will need several key variables:
* `warehouse_name` — the name of the warehouse you will create (e.g. `SYNQ_WH`)
* `database_name` as an identifier of database (or multiple) you want to monitor
The following setup does several separate steps:
1. It creates a dedicated `username` and `role` for Coalesce Quality
2. It creates `xsmall` data warehouse that we can use to query your metadata
3. Grant necessary privileges to the dedicated user and role so we can pull query history and monitor the target database
## **Setup a user and warehouse**
As a super user, execute the following SQL commands to create a read-only role, a user assigned to that role, and a warehouse for that role.
We recommend creating a dedicated user name and warehouse.
**Use key-pair authentication.** Snowflake is [retiring single-factor password sign-in](https://docs.snowflake.com/en/user-guide/security-mfa-rollout): every human user must enrol in MFA, and service users lose password authentication altogether. Coalesce Quality connects unattended and cannot supply a second factor, so a password connection fails with error `394508` once your account reaches that stage. The scripts below create the user as `TYPE = SERVICE`, which is exempt from MFA and accepts key-pair authentication only.
If you already connect with a password, follow [Password authentication is deprecated by Snowflake](#password-authentication-is-deprecated-by-snowflake) to move the existing user to key-pair without recreating it.
### Key-pair authentication
For more details on Snowflake's key-pair authentication, see the [official Snowflake documentation](https://docs.snowflake.com/en/user-guide/key-pair-auth).
Coalesce Quality supports two key-pair authentication methods:
1. **Managed Key-pair**: The key pair is generated and managed for you
2. **User Managed Key-pair**: You generate and provide your own private key (supports both encrypted and unencrypted keys)
#### Option A: Managed Key-pair
When configuring the integration, select "Managed Key-pair" and save it. Coalesce Quality then generates the key pair and shows the public half under **Key pair authentication** at the bottom of the integration's page. Copy it and use it in the setup below.
In the code, remember to replace:
* `` with the key generated in the previous step
* `` with the name you want the dedicated warehouse to have
```sql theme={null}
-- Configuration
set user_name='SYNQ';
set rsa_public_key='';
set warehouse_size='XSMALL';
set warehouse_name='';
set role_name='SYNQ';
-- Set role for grants
USE ROLE ACCOUNTADMIN;
-- Create warehouse for the monitoring workload
CREATE WAREHOUSE IF NOT EXISTS identifier($warehouse_name)
warehouse_size=$warehouse_size
auto_resume = true
auto_suspend = 60
initially_suspended=true
;
-- Create the dedicated role
CREATE ROLE IF NOT EXISTS identifier($role_name);
-- Create the dedicated service user and grant access to role.
-- TYPE = SERVICE exempts it from Snowflake's MFA enforcement.
CREATE USER IF NOT EXISTS identifier($user_name) TYPE = SERVICE RSA_PUBLIC_KEY=$rsa_public_key DEFAULT_ROLE=$role_name;
GRANT ROLE identifier($role_name) TO USER identifier($user_name);
-- Grant permissions to use the new warehouse
GRANT OPERATE, USAGE, MONITOR ON WAREHOUSE identifier($warehouse_name) TO ROLE identifier($role_name);
-- Grant privileges to allow access to query history
GRANT IMPORTED PRIVILEGES ON DATABASE "SNOWFLAKE" TO ROLE identifier($role_name);
```
`CREATE USER IF NOT EXISTS` leaves an existing user untouched, public key included. If the user already exists, attach the key with `ALTER USER` instead — see [moving an existing user to key-pair](#password-authentication-is-deprecated-by-snowflake).
#### Option B: User Managed Key-pair
Both **encrypted** and **unencrypted** private keys in PKCS#8 PEM format are supported. If you use an encrypted key, you'll need to provide the passphrase when configuring the integration.
##### Generate a new key pair
Snowflake requires a minimum 2048-bit RSA private key in PKCS#8 PEM format.
**Generate an unencrypted private key:**
```bash theme={null}
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
```
**Generate an encrypted private key (recommended for better security):**
```bash theme={null}
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8
```
When prompted, enter a passphrase to encrypt the private key. You'll need to provide this passphrase when configuring the integration.
##### Extract the public key
After generating your private key, extract the public key:
```bash theme={null}
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
```
##### Configure Snowflake with your public key
Remove the header and footer from the public key file (`-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----`), then use the remaining content as `` in the setup script:
```sql theme={null}
-- Configuration
set user_name='SYNQ';
set rsa_public_key='';
set warehouse_size='XSMALL';
set warehouse_name='';
set role_name='SYNQ';
-- Set role for grants
USE ROLE ACCOUNTADMIN;
-- Create warehouse for the monitoring workload
CREATE WAREHOUSE IF NOT EXISTS identifier($warehouse_name)
warehouse_size=$warehouse_size
auto_resume = true
auto_suspend = 60
initially_suspended=true
;
-- Create the dedicated role
CREATE ROLE IF NOT EXISTS identifier($role_name);
-- Create the dedicated service user and grant access to role.
-- TYPE = SERVICE exempts it from Snowflake's MFA enforcement.
CREATE USER IF NOT EXISTS identifier($user_name) TYPE = SERVICE RSA_PUBLIC_KEY=$rsa_public_key DEFAULT_ROLE=$role_name;
GRANT ROLE identifier($role_name) TO USER identifier($user_name);
-- Grant permissions to use the new warehouse
GRANT OPERATE, USAGE, MONITOR ON WAREHOUSE identifier($warehouse_name) TO ROLE identifier($role_name);
-- Grant privileges to allow access to query history
GRANT IMPORTED PRIVILEGES ON DATABASE "SNOWFLAKE" TO ROLE identifier($role_name);
```
##### Configure the integration
In the integration settings:
1. Select "User Managed Key-pair" as the authentication method
2. Paste your **private key** (including the `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` headers)
3. If your key is encrypted (starts with `-----BEGIN ENCRYPTED PRIVATE KEY-----`), provide the passphrase
Keys in PKCS#1 format (`-----BEGIN RSA PRIVATE KEY-----`) are not supported by Snowflake. If you have a PKCS#1 key, convert it to PKCS#8 format using:
```bash theme={null}
openssl pkcs8 -topk8 -inform PEM -in rsa_key.pem -out rsa_key.p8 -nocrypt
```
#### Rotating a managed key pair
A managed key pair (Option A) is replaced from Settings → Integrations → *(your Snowflake integration)* → **Key pair authentication**, at the bottom of the page. Generating a replacement changes nothing until you activate it, so the integration keeps connecting with the key in use meanwhile.
1. Press **Generate a new key pair**. The new public key appears under *New public key, waiting to be used*.
2. Add that public key to the Snowflake user the integration connects as, without replacing the key in use. Follow Snowflake's [key-pair rotation guide](https://docs.snowflake.com/en/user-guide/key-pair-auth#configuring-key-pair-rotation).
3. Press **Start using the new key pair**.
4. Remove the old public key from the Snowflake user, as the same guide describes.
Add the new public key on the Snowflake user before you activate it. Activating first leaves the integration unable to connect until you do.
To drop a replacement you decided against, press **Discard it**.
A user-managed key pair (Option B) is rotated on your side: follow Snowflake's [key-pair rotation guide](https://docs.snowflake.com/en/user-guide/key-pair-auth#configuring-key-pair-rotation), then paste the new private key into the integration.
### Password authentication (deprecated)
Snowflake is removing password authentication for service users as part of its [single-factor password deprecation](https://docs.snowflake.com/en/user-guide/security-mfa-rollout). A password connection works only while your account still permits `TYPE = LEGACY_SERVICE` users, and stops with error `394508` when it no longer does. Prefer key-pair authentication above; if you must start with a password, plan to [move the user to key-pair](#password-authentication-is-deprecated-by-snowflake).
In the code, remember to replace:
* `` with a password that meets your account's [password policy](https://docs.snowflake.com/en/sql-reference/sql/create-password-policy)
* `` with the name you want the dedicated warehouse to have
```sql theme={null}
-- Configuration
set user_name='SYNQ';
set user_password='';
set warehouse_size='XSMALL';
set warehouse_name='';
set role_name='SYNQ';
-- Set role for grants
USE ROLE ACCOUNTADMIN;
-- Create warehouse for the monitoring workload
CREATE WAREHOUSE IF NOT EXISTS identifier($warehouse_name)
warehouse_size=$warehouse_size
auto_resume = true
auto_suspend = 60
initially_suspended=true
;
-- Create the dedicated role
CREATE ROLE IF NOT EXISTS identifier($role_name);
-- Create the dedicated user and grant access to role.
-- LEGACY_SERVICE is the only user type Snowflake still signs in with a
-- password alone, and it is being retired.
CREATE USER IF NOT EXISTS identifier($user_name) TYPE = LEGACY_SERVICE PASSWORD=$user_password DEFAULT_ROLE=$role_name;
GRANT ROLE identifier($role_name) TO USER identifier($user_name);
-- Grant permissions to use the new warehouse
GRANT OPERATE, USAGE, MONITOR ON WAREHOUSE identifier($warehouse_name) TO ROLE identifier($role_name);
-- Grant privileges to allow access to query history
GRANT IMPORTED PRIVILEGES ON DATABASE "SNOWFLAKE" TO ROLE identifier($role_name);
```
## Password authentication is deprecated by Snowflake
If the integration stops connecting, or a new one never does, with
```
394508 (08004): Failed to authenticate: MFA with TOTP is required. To authenticate, provide both your password and a current TOTP passcode.
```
(or `394509`, `390197`, `250001 Multi-factor authentication is required for this account`), Snowflake has enforced its [single-factor password deprecation](https://docs.snowflake.com/en/user-guide/security-mfa-rollout) on the user the integration signs in as. Every human user (`TYPE = PERSON`, or no type) must now present a second factor, and password authentication is being removed from `LEGACY_SERVICE` users. Coalesce Quality connects unattended and cannot enter a TOTP code, so no password will work. Being able to log into Snowflake yourself does not help: your own login satisfies MFA interactively, the integration cannot.
Service users (`TYPE = SERVICE`) are exempt from MFA and authenticate with a key pair. The fix is to move the integration's user to key-pair authentication; it does not need to be recreated.
### Move an existing user to key-pair
1. In Coalesce Quality, open Settings → Integrations → *(your Snowflake integration)*, set **Authentication method** to **Coalesce Quality managed key-pair** and save. The public key appears under **Key pair authentication** at the bottom of the page; copy it. (To bring your own key instead, pick **User managed key-pair** and generate one as in [Option B](#option-b-user-managed-key-pair).)
2. In Snowflake, as `ACCOUNTADMIN` or a role with `OWNERSHIP` on the user, attach the key to the existing user:
```sql theme={null}
set user_name='SYNQ';
set rsa_public_key='';
USE ROLE ACCOUNTADMIN;
ALTER USER identifier($user_name) SET RSA_PUBLIC_KEY=$rsa_public_key;
```
3. Back in Coalesce Quality, press **Test connection**. Once it succeeds, remove the password and mark the user as a service user so it stays outside MFA enforcement:
```sql theme={null}
ALTER USER identifier($user_name) UNSET PASSWORD;
ALTER USER identifier($user_name) SET TYPE = SERVICE;
```
Do not re-run the setup script for this. It creates the user with `CREATE USER IF NOT EXISTS`, which skips a user that already exists, so the public key is never attached and the connection keeps failing with the same error.
### If the integration signs in as a person
If the integration was set up with someone's own Snowflake login rather than a dedicated user, do not try to exempt that login from MFA. Create a dedicated service user with the [key-pair setup](#key-pair-authentication) above, grant it the integration's role, and update the **Username** on the integration to the new user.
## **Grant access to monitored database**
We recommend setting access on Schema level, as long as it is aligned with your permissions management strategy.
Applying schema level future grants in a Snowflake account where there are only database future grants can break existing roles! From [Snowflake's docs](https://docs.snowflake.com/en/sql-reference/sql/grant-privilege.html#considerations):
*"When future grants are defined at both the database and schema level, the schema level grants take precedence over the database level grants, and the database level grants are ignored. An important point to note here is that as long as there is a SCHEMA level future grants, ALL DATABASE levels will be ignored, even for the roles that are NOT defined in the SCHEMA level future grants."*
If you use schema grants follow Option 1, if you use database grants follow Option 2.
### Option 1 (Recommended): Grant access to all schemas
The following stored procedure iterates through each schema in a given database, then grants access to each table in a given schema. This stored procedure should be run as follows each time a new schema is created. Run it in the same Worksheet as the **Setup a user and warehouse** step above so the `$role_name` session variable is still set.
Replace `` with the name of your database.
```sql theme={null}
set database_name='';
-- Grant metadata privileges to database to be monitored
GRANT USAGE,MONITOR ON DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT USAGE,MONITOR ON ALL SCHEMAS IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT USAGE,MONITOR ON FUTURE SCHEMAS IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
USE DATABASE identifier($database_name);
CREATE OR REPLACE PROCEDURE grantFutureAccess(databaseName string, roleName string)
returns string not null
language javascript
as
$$
var schemaResultSet = snowflake.execute({ sqlText: 'SELECT SCHEMA_NAME FROM ' + DATABASENAME + ".INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME != 'INFORMATION_SCHEMA';"});
var numberOfSchemasGranted = 0;
while (schemaResultSet.next()) {
numberOfSchemasGranted += 1;
var schemaAndRoleSuffix = ' in schema ' + DATABASENAME + '."' +
schemaResultSet.getColumnValue('SCHEMA_NAME') + '" to role ' + ROLENAME + ';'
snowflake.execute({ sqlText: 'grant USAGE on schema ' + DATABASENAME + '."' +
schemaResultSet.getColumnValue('SCHEMA_NAME') + '" to role ' + ROLENAME + ';'});
snowflake.execute({ sqlText: 'grant SELECT on all tables' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on all views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on all materialized views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on all dynamic tables' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on all streams' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on all semantic views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant REFERENCES on all semantic views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant USAGE on all procedures' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant USAGE on all functions' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on future tables' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on future views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on future materialized views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on future dynamic tables' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on future streams' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant SELECT on future semantic views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant REFERENCES on future semantic views' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant USAGE on future procedures' + schemaAndRoleSuffix});
snowflake.execute({ sqlText: 'grant USAGE on future functions' + schemaAndRoleSuffix});
}
return 'Granted access to ' + numberOfSchemasGranted + ' schemas';
$$
;
grant USAGE on database identifier($database_name) to role identifier($role_name);
call grantFutureAccess($database_name, $role_name);
```
`MATERIALIZED VIEW`, `STREAM`, and `SELECT ON DYNAMIC TABLE` grants are silently ignored on Snowflake editions that do not support those object types — the `grant` statements still succeed, so the procedure is safe to run on Standard edition.
**Semantic views need both `SELECT` and `REFERENCES`.** `SELECT` lets the role list them via `SHOW SEMANTIC VIEWS` and query them via `SEMANTIC_VIEW(sv DIMENSIONS … METRICS …)`. `REFERENCES` is required separately so `GET_DDL('SEMANTIC_VIEW', …)` returns the body — without it, the catalog scrape can see the asset but cannot read its definition. Snowflake gates this differently from every other object kind, where `SELECT` / `USAGE` cover both visibility and `GET_DDL`.
### Option 2: Grant access to a database
Run this in the same Worksheet as the **Setup a user and warehouse** step above.
Replace `` with the name of your database.
```sql theme={null}
set database_name='';
-- Read-only access to database
grant USAGE, MONITOR on database identifier($database_name) to role identifier($role_name);
grant USAGE, MONITOR on all schemas in database identifier($database_name) to role identifier($role_name);
grant USAGE, MONITOR on future schemas in database identifier($database_name) to role identifier($role_name);
grant SELECT on all tables in database identifier($database_name) to role identifier($role_name);
grant SELECT on future tables in database identifier($database_name) to role identifier($role_name);
grant SELECT on all views in database identifier($database_name) to role identifier($role_name);
grant SELECT on future views in database identifier($database_name) to role identifier($role_name);
grant SELECT on all materialized views in database identifier($database_name) to role identifier($role_name);
grant SELECT on future materialized views in database identifier($database_name) to role identifier($role_name);
grant SELECT on all dynamic tables in database identifier($database_name) to role identifier($role_name);
grant SELECT on future dynamic tables in database identifier($database_name) to role identifier($role_name);
grant SELECT on all streams in database identifier($database_name) to role identifier($role_name);
grant SELECT on future streams in database identifier($database_name) to role identifier($role_name);
-- Semantic views: SELECT enables SHOW + SEMANTIC_VIEW(...) querying;
-- REFERENCES is required separately for GET_DDL('SEMANTIC_VIEW', ...).
grant SELECT on all semantic views in database identifier($database_name) to role identifier($role_name);
grant SELECT on future semantic views in database identifier($database_name) to role identifier($role_name);
grant REFERENCES on all semantic views in database identifier($database_name) to role identifier($role_name);
grant REFERENCES on future semantic views in database identifier($database_name) to role identifier($role_name);
grant USAGE on all procedures in database identifier($database_name) to role identifier($role_name);
grant USAGE on future procedures in database identifier($database_name) to role identifier($role_name);
grant USAGE on all functions in database identifier($database_name) to role identifier($role_name);
grant USAGE on future functions in database identifier($database_name) to role identifier($role_name);
```
## **Grant access to Snowflake tasks (Optional)**
If you want to monitor Snowflake tasks, you need to grant MONITOR privileges on the specific tasks you want to observe. This allows us to collect task execution history and metadata.
Replace ``, ``, and `` with your actual database, schema, and task names before running:
```sql theme={null}
-- Grant MONITOR privilege on a specific task
GRANT MONITOR ON TASK .. TO ROLE identifier($role_name);
-- To grant MONITOR on all tasks in a schema (if needed)
GRANT MONITOR ON ALL TASKS IN SCHEMA . TO ROLE identifier($role_name);
-- To grant MONITOR on future tasks in a schema (if needed)
GRANT MONITOR ON FUTURE TASKS IN SCHEMA . TO ROLE identifier($role_name);
```
The MONITOR privilege on tasks allows viewing task execution history, status, and metadata but does not allow executing or modifying the tasks. This is required for task monitoring and anomaly detection.
After granting these privileges, you must also enable task monitoring in the integration settings. In the integration configuration screen, make sure to enable "Fetch tasks and their executions" to start collecting task data.
## **Grant access to Snowflake tags (Optional)**
If your Snowflake objects use [tags](https://docs.snowflake.com/en/user-guide/object-tagging) and you want Coalesce Quality to read them (e.g. to filter tables or deploy monitors based on tag values), you need to grant the `APPLY TAG` privilege. Without this privilege, Snowflake's [`GET_DDL`](https://docs.snowflake.com/en/sql-reference/functions/get_ddl#usage-notes) function replaces tag names and values with `#UNKNOWN_TAG='#UNKNOWN_VALUE'` placeholders.
```sql theme={null}
-- Grant ability to read tags from GET_DDL output
GRANT APPLY TAG ON ACCOUNT TO ROLE identifier($role_name);
```
This grant is only needed if you use Snowflake tags on your tables/views and want Coalesce Quality to use them for filtering or monitor rules. Coalesce Quality will show a warning during ingestion if it detects unknown tag placeholders in your metadata, so you can decide at that point whether to apply this grant.
The `APPLY TAG` privilege allows reading tag assignments but does not grant the ability to modify data or other security policies. See the [Snowflake GET\_DDL documentation](https://docs.snowflake.com/en/sql-reference/functions/get_ddl#usage-notes) for more details.
## **Grant access to stages (Optional)**
Coalesce Quality shows each named stage as an asset, with the location it points at, and the lineage between the stage and the tables `COPY INTO` loads from it or unloads to it (this needs query logs enabled). Snowflake lists a stage only to a role holding a privilege on it, so stages need a grant of their own:
```sql theme={null}
-- External stages in every schema of the database, now and in the future
GRANT USAGE ON ALL STAGES IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT USAGE ON FUTURE STAGES IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
```
Unlike the baseline grants, `USAGE` on an external stage also lets the role read the files in it, through `LIST @stage` or `SELECT … FROM @stage`. Coalesce Quality only runs `SHOW STAGES`, but grant it only if that access is acceptable, or grant it on the specific stages you want to see:
```sql theme={null}
GRANT USAGE ON STAGE .. TO ROLE identifier($role_name);
```
`USAGE` applies to external stages only. An internal stage is listed only to a role with `READ` on it, which is read access to its files, and it has no external location to show, so we do not ask for it. The user stage (`@~`) and table stages (`@%table`) are not objects of their own and never appear.
If your schemas use schema-level future grants (Option 1 above), use `GRANT USAGE ON FUTURE STAGES IN SCHEMA …` per schema instead, for the reason explained there.
## **Grant access to apps, notebooks, warehouses and pipes (Optional)**
With **Fetch apps, notebooks, warehouses and pipes** enabled, Coalesce Quality shows each Streamlit app, notebook, warehouse, pipe and Cortex Search service as an asset: an app under its title, with the warehouse it runs on; a pipe with the stage it reads and the table it loads; a Cortex Search service with the tables its query reads. Snowflake lists each of them only to a role holding a privilege on it:
```sql theme={null}
-- Streamlit apps and Cortex Search services, now and in the future
GRANT USAGE ON ALL STREAMLITS IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT USAGE ON FUTURE STREAMLITS IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT USAGE ON ALL CORTEX SEARCH SERVICES IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT USAGE ON FUTURE CORTEX SEARCH SERVICES IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
-- Pipes created from now on. Snowflake refuses a grant on all existing pipes
-- at once, so grant those one by one.
GRANT MONITOR ON FUTURE PIPES IN DATABASE identifier($database_name) TO ROLE identifier($role_name);
GRANT MONITOR ON PIPE .. TO ROLE identifier($role_name);
-- Each warehouse you want to see
GRANT MONITOR ON WAREHOUSE TO ROLE identifier($role_name);
```
`MONITOR` lets the role see a pipe's status and a warehouse's load, not change them. `USAGE` on a Streamlit app lets the role open the app, and on a Cortex Search service lets it query the service; Coalesce Quality only lists them.
Snowflake takes no grant on a notebook, so a notebook is listed only when the integration's role owns it.
If your schemas use schema-level future grants (Option 1 above), use the `… IN SCHEMA …` form of the future grants per schema instead, for the reason explained there.
## **Stored procedures and user-defined functions**
Coalesce Quality automatically discovers stored procedures and UDFs using `SHOW PROCEDURES IN DATABASE` / `SHOW USER FUNCTIONS IN DATABASE` and fetches each object's DDL via `GET_DDL`. All languages are supported (SQL, JavaScript, Python, Java, Scala). The grants in Option 1 / Option 2 above already include the required `USAGE` on procedures and functions — no extra steps are needed.
Procedures and UDFs show up as first-class assets in Coalesce Quality and participate in lineage whenever they are referenced by tables, views, or tasks.
## Personal access (user OAuth)
In addition to the workspace credentials above, you can let each Coalesce Quality member authenticate as themselves against Snowflake. Queries then run under the member's own Snowflake account and respect the grants, masking policies, and row-level security configured on that user. See [Data Warehouse Access](/security/dwh-access) for the full model and when to enable it.
To enable personal access, an account admin creates a Snowflake security integration of type `OAUTH` once, then pastes the client ID and secret into the Snowflake integration's edit page in Coalesce Quality.
### Create the security integration
```sql theme={null}
USE ROLE ACCOUNTADMIN;
CREATE SECURITY INTEGRATION COALESCE_QUALITY_USER_OAUTH
TYPE = OAUTH
ENABLED = TRUE
OAUTH_CLIENT = CUSTOM
OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
OAUTH_REDIRECT_URI = 'https://app.synq.io/auth/dwh-oauth/callback'
OAUTH_ALTERNATE_REDIRECT_URIS = ('https://app.us.synq.io/auth/dwh-oauth/callback', 'https://app.au.synq.io/auth/dwh-oauth/callback')
OAUTH_ISSUE_REFRESH_TOKENS = TRUE
OAUTH_REFRESH_TOKEN_VALIDITY = 7776000 -- 90 days
BLOCKED_ROLES_LIST = ('ACCOUNTADMIN','ORGADMIN','SECURITYADMIN');
```
Keep only the redirect URI(s) for the Coalesce Quality region(s) your members use. The EU app is `https://app.synq.io`, the US app is `https://app.us.synq.io` and the AU app is `https://app.au.synq.io` — pass one as `OAUTH_REDIRECT_URI` and any others in `OAUTH_ALTERNATE_REDIRECT_URIS`. No trailing slash.
`BLOCKED_ROLES_LIST` prevents members from minting OAuth tokens for high-privilege account roles. Add any other roles your security policy reserves for break-glass use.
### Fetch the client credentials
```sql theme={null}
SELECT SYSTEM$SHOW_OAUTH_CLIENT_SECRETS('COALESCE_QUALITY_USER_OAUTH');
```
The function returns a JSON object with `OAUTH_CLIENT_ID`, `OAUTH_CLIENT_SECRET`, and `OAUTH_CLIENT_SECRET_2` (the second secret exists for rotation; either can be used). Paste the client ID and one of the secrets into Settings → Integrations → *(your Snowflake integration)* → Warehouse access → Personal credentials in Coalesce Quality.
No user mapping is required. When a member consents, they sign in to Snowflake on the standard authorize page, and the access token is issued for that Snowflake user. If your account uses SSO/SAML, the consent page transparently redirects through the IdP. Coalesce Quality stores per-user access/refresh tokens; queries from that member then run as the Snowflake user they signed in as.
### Optional: restrict which roles members can request
Each member can pass a `role` parameter when consenting (Coalesce Quality forwards it as `scope=session:role:`). If you want to constrain the set of roles reachable through this OAuth app, use `ALLOWED_ROLES_LIST`:
```sql theme={null}
ALTER SECURITY INTEGRATION COALESCE_QUALITY_USER_OAUTH
SET ALLOWED_ROLES_LIST = ('ANALYST', 'READER');
```
`ALLOWED_ROLES_LIST` is a whitelist that complements `BLOCKED_ROLES_LIST` (blacklist) above. Leave it empty to allow any role the member is granted on Snowflake, minus the blocked ones.
## Databases from shares
When an account creates a database from a share (`CREATE DATABASE … FROM SHARE`), Snowflake records only the share it came from, not the database in the account that provides it. To link the tables of such a database to their source tables, Coalesce Quality needs to know which database each share was made from. You give it that in the integration of the account that **receives** the share, under *Advanced* → *Databases from shares*.
The values come from `SHOW SHARES`, run in the account that **provides** the share:
```sql theme={null}
USE ROLE ACCOUNTADMIN; -- or the role that owns the shares
SHOW SHARES;
```
Use the rows of kind `OUTBOUND`. Each one gives the three values the integration asks for:
| `SHOW SHARES` column | Field in the integration |
| - | - |
| `owner_account` | Owner account |
| `name` | Share name |
| `database_name` | Shared database |
The easiest way is **Fill from SHOW SHARES**: paste the whole result, as copied from Snowsight, SnowSQL or the Snowflake CLI, and Coalesce Quality suggests the shares it found in it. Check each suggestion, correct anything it read wrong, and untick the shares this account doesn't import. Nothing is saved until you save the integration. You can also add each share by hand.
The integration role cannot run `SHOW SHARES` for you. Snowflake lists outbound shares only to a role with the `IMPORT SHARE` privilege, and then only the shares that role owns, so `ACCOUNTADMIN` is usually the only role that sees all of them. Running it from a stored procedure with owner's rights is not allowed either. That's why the values are entered in the integration rather than read by it.
A share without an entry is skipped: its database is still monitored, but its tables are not linked to the tables in the providing account. Add the new share here whenever the providing account starts sharing another database.
## Network Configuration
If your Snowflake instance is behind a firewall or has network access controls, you may need to whitelist our IP addresses. See [IP Whitelist](/security/ip) for the complete list of IP addresses by region.
Once you've successfully created the warehouse, database, and dedicated role, input the following data in the Coalesce Quality UI.
## Input data in the UI
### Integration name
For example, `snowflake`
### Account name
You can find it under `Admin` > `Accounts` when you highlight the link icon (🔗). Both formats work:
* `-` (preferred, e.g. `acme-prod1`)
* `.` (legacy, e.g. `xy12345.eu-west-1`)
Do **not** include `https://` or a trailing slash. A trailing `.snowflakecomputing.com` is stripped and the value is lowercased automatically, so either `acme-prod1` or `acme-prod1.snowflakecomputing.com` will work.
### Databases
The names of the databases you granted access to earlier in the guide. You can monitor multiple databases with the same integration.
### Role, username, and authentication
The role name and username you created earlier in the guide, and the authentication method that matches how you set the user up: a key pair (recommended), or the password for a `LEGACY_SERVICE` user while your Snowflake account still permits one.
### Warehouse
The name of the warehouse you created earlier in the guide.
### Audit table FQN (optional)
Fully-qualified table name (e.g. `MY_DB.PUBLIC.QUALITY_SQL_TEST__AUDIT`) where Coalesce Quality will write SQL test execution results. The integration creates the table automatically if it does not yet exist. Leave empty if you do not run SQL tests.
### ACCOUNT\_USAGE database name (optional)
Defaults to `SNOWFLAKE`. Override only if your account has the `SNOWFLAKE` share imported under a non-default name.
### Optional flags
* **Fetch query logs** — enables query history ingestion from `SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY`. You can optionally restrict the set of source databases.
* **Fetch Snowflake tasks** — discovers Snowflake Tasks (`SHOW TASKS`). Select a subset of databases or leave empty for all.
* **Fetch task executions from `ACCOUNT_USAGE.TASK_HISTORY`** — when on, centralized task history is used (up to 45-minute lag). When off, `INFORMATION_SCHEMA.TASK_HISTORY` is queried per database (lower lag, more warehouse time).
* **Task execution fetch interval (minutes)** — minimum 5.
* **Fetch table lineage** — uses `SNOWFLAKE.CORE.GET_LINEAGE` for table-level lineage (Enterprise+). Optionally restrict to a subset of databases.
* **Fetch apps, notebooks, warehouses and pipes** — with every catalog refresh, also lists Streamlit apps, notebooks, warehouses, pipes and Cortex Search services. Needs the grants in [Grant access to apps, notebooks, warehouses and pipes](#grant-access-to-apps-notebooks-warehouses-and-pipes-optional).
* **Don't use GET\_DDL for table schema collection** — enable only if `GET_DDL` causes warehouse pressure; Coalesce Quality will fall back to parsing query logs.
# Trino
Source: https://docs.synq.io/dw-integrations/trino
Integrating Trino (Iceberg) with Coalesce Quality
This guide will show you how to securely connect Coalesce Quality to your Trino instance.
We need this information so we collect metadata about your tables.
To be able to finish this guide, you’ll need the following:
→ Access to modify your Trino configuration as account admin
⏱️ Estimated time to finish: 10 minutes.
## Data we collect
For the automated data anomaly testing we collect the following:
* Number of rows in every table in a given dataset
* Timestamp of the last change of data in all tables in a given dataset
You will need several key variables:
* `host` Host of your Trino cluster, e.g. host.trino.com
* `port` Port of your Trino cluster. If not specified the Trino driver will decide which port to use
* `username`
* `password`
# Google Cloud Storage
Source: https://docs.synq.io/gcp-integrations/cloud-storage
Automatically discover and track GCS buckets in Coalesce Quality
## Overview
The Coalesce Quality Google Cloud Storage integration automatically discovers all GCS buckets in your Google Cloud project and creates custom entities for centralized asset management and governance.
This integration can also serve as a reference implementation for building custom integrations using the [Coalesce Quality Public API](/api-reference/getting-started).
**Source code:** [github.com/getsynq/synq-google-cloud-storage](https://github.com/getsynq/synq-google-cloud-storage)
## What It Does
The integration:
* **Discovers** all GCS buckets in your Google Cloud project
* **Creates** custom entities for each bucket with rich metadata
* **Syncs** comprehensive bucket information including:
* Storage class and location
* Versioning status
* Lifecycle rules with detailed conditions
* Uniform bucket-level access settings
* User-defined labels
* **Filters** buckets based on include/exclude patterns
* **Auto-cleanup** removes entities when buckets are deleted
Each bucket entity includes a detailed description with storage class, location, creation date, versioning status, and complete lifecycle rule configurations.
## Use Cases
* **Data Catalog Management**: Track all cloud storage resources in one place
* **Resource Discovery**: Automatically maintain an up-to-date inventory of GCS buckets
* **Compliance Tracking**: Monitor storage configurations and lifecycle policies
* **Cross-Platform Lineage**: Connect storage buckets with data pipelines and tables
## Installation
### Download Pre-built Binaries
Download the latest release for your platform from the [releases page](https://github.com/getsynq/synq-google-cloud-storage/releases).
**Intel:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-storage/releases/latest/download/synq-google-cloud-storage_darwin_amd64.tar.gz
tar -xzf synq-google-cloud-storage_darwin_amd64.tar.gz
sudo mv synq-google-cloud-storage /usr/local/bin/
```
**Apple Silicon:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-storage/releases/latest/download/synq-google-cloud-storage_darwin_arm64.tar.gz
tar -xzf synq-google-cloud-storage_darwin_arm64.tar.gz
sudo mv synq-google-cloud-storage /usr/local/bin/
```
**AMD64:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-storage/releases/latest/download/synq-google-cloud-storage_linux_amd64.tar.gz
tar -xzf synq-google-cloud-storage_linux_amd64.tar.gz
sudo mv synq-google-cloud-storage /usr/local/bin/
```
**ARM64:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-storage/releases/latest/download/synq-google-cloud-storage_linux_arm64.tar.gz
tar -xzf synq-google-cloud-storage_linux_arm64.tar.gz
sudo mv synq-google-cloud-storage /usr/local/bin/
```
Download the `.zip` file from the [releases page](https://github.com/getsynq/synq-google-cloud-storage/releases) and extract it.
Requires Go 1.24 or later:
```bash theme={null}
git clone https://github.com/getsynq/synq-google-cloud-storage.git
cd synq-google-cloud-storage
go build
```
## Configuration
### Required: API Credentials
Create a `.env` file with your Coalesce Quality API credentials:
```bash theme={null}
SYNQ_CLIENT_ID=your_client_id_here
SYNQ_CLIENT_SECRET=your_client_secret_here
# GCP Project ID (optional if running on GCP or using gcloud)
GCP_PROJECT_ID=your-gcp-project-id
```
The GCP project ID can be auto-detected from:
* Environment variables (`GCP_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`, `GCLOUD_PROJECT`)
* `gcloud` CLI configuration
* GCP metadata server (when running on GCP)
### Optional: Advanced Configuration
For advanced customization, create a `config.yaml` file:
```yaml theme={null}
# Coalesce Quality API Configuration (defaults shown for EU region)
synq:
endpoint: "developer.synq.io:443" # EU region (default)
# For US region, use: "api.us.synq.io:443"; for AU, "api.au.synq.io:443"
oauth_url: "https://developer.synq.io/oauth2/token" # EU region (default)
# For US region, use: "https://api.us.synq.io/oauth2/token"; for AU, "https://api.au.synq.io/oauth2/token"
# GCP Configuration
gcp:
user_agent: "synq-gcs-client-v1.0.0"
# project_id: "your-project-id" # Alternative to GCP_PROJECT_ID env var
# entity_group_id: "gcs::custom-group-id" # Defaults to gcs::
# Custom Entity Type IDs
types:
bucket_type_id: 40
# bucket_icon: "path/to/custom-bucket-icon.svg"
# Resource Filters
filter:
buckets:
include: [] # Empty means include all
exclude: [] # Regex patterns to exclude
# Examples:
# include: ["prod-.*"] # Only include buckets starting with prod-
# exclude: ["test-.*"] # Skip buckets starting with test-
```
**Configuration precedence:** defaults → config.yaml → environment variables → CLI flags
## Network Requirements
If your GCP project has firewall rules that restrict outbound connections, whitelist the Coalesce Quality egress IP addresses:
* App: [https://app.synq.io](https://app.synq.io)
* API: [https://developer.synq.io](https://developer.synq.io)
* **Egress IP: `34.105.135.39`**
* App: [https://app.us.synq.io](https://app.us.synq.io)
* API: [https://api.us.synq.io](https://api.us.synq.io)
* **Egress IP: `35.238.250.82`**
* App: [https://app.au.synq.io](https://app.au.synq.io)
* API: [https://api.au.synq.io](https://api.au.synq.io)
* **Egress IP: `35.189.4.22`**
See [IP Addresses](/security/ip) for the latest information.
## Running the Integration
### Basic Usage
```bash theme={null}
# Minimal setup: just create .env with credentials
cp .env.example .env
# Edit .env and add your credentials
# Run with defaults
./synq-google-cloud-storage
# Run with debug logging
LOG_LEVEL=DEBUG ./synq-google-cloud-storage
# Run with JSON logging for production
LOG_FORMAT=json ./synq-google-cloud-storage
```
### Dry-Run Mode
Preview what entities would be created without making API calls:
```bash theme={null}
# Dry-run mode (no API calls)
./synq-google-cloud-storage --dry-run
# Dry-run with debug logging
LOG_LEVEL=DEBUG ./synq-google-cloud-storage --dry-run
```
In dry-run mode, the integration scans GCS buckets and shows what would be created, but does not call the API or require credentials.
### Command-Line Options
Common flags:
* `--dry-run` - Preview changes without calling the API
* `--gcp.project-id` - Specify GCP project ID
* `--filter.buckets.include` - Bucket name patterns to include
* `--filter.buckets.exclude` - Bucket name patterns to exclude
* `--types.bucket-type-id` - Custom entity type ID (default: 40)
* `--synq.endpoint` - API endpoint for your region
For US region:
```bash theme={null}
./synq-google-cloud-storage \
--synq.endpoint=api.us.synq.io:443 \
--synq.oauth-url=https://api.us.synq.io/oauth2/token
```
For AU region:
```bash theme={null}
./synq-google-cloud-storage \
--synq.endpoint=api.au.synq.io:443 \
--synq.oauth-url=https://api.au.synq.io/oauth2/token
```
Run `./synq-google-cloud-storage --help` for all available options.
## How It Works
1. **Authenticates** with the Coalesce Quality API using OAuth2 client credentials
2. **Creates/updates** custom entity type for GCS buckets
3. **Discovers** all GCS buckets in your project
4. **Creates entities** with comprehensive metadata
5. **Uses entity groups** to enable automatic cleanup of deleted buckets
### Entity Groups
The integration uses entity groups to track all entities created in each run. When the group is updated, Coalesce Quality automatically removes entities that were in the previous group but not in the current one, enabling automatic cleanup of deleted buckets.
### Custom Identifiers
All entities use custom identifiers with `gcs::` prefix for namespace isolation. Buckets use identifiers in the format: `gcs::`.
## Scheduling with Cron
To keep your bucket inventory up-to-date, schedule the integration to run periodically:
```bash theme={null}
# Run every hour
0 * * * * cd /path/to/integration && ./synq-google-cloud-storage
# Run every 6 hours
0 */6 * * * cd /path/to/integration && ./synq-google-cloud-storage
```
## Using as a Public API Example
This integration demonstrates best practices for using the Coalesce Quality Public API:
* OAuth2 authentication with client credentials
* Creating and managing custom entity types
* Using entity groups for automatic cleanup
* Implementing filtering and dry-run modes
* Handling regional deployments
See the [source code](https://github.com/getsynq/synq-google-cloud-storage) and [API documentation](/api-reference/getting-started) for more details.
## Troubleshooting
Verify your credentials are correct in the `.env` file. Ensure you're using the correct endpoint for your region (EU or US).
Set the `GCP_PROJECT_ID` environment variable explicitly or configure `gcloud` CLI with your project:
```bash theme={null}
gcloud config set project YOUR_PROJECT
```
Ensure your GCP credentials have the `storage.buckets.list` permission and can read bucket metadata.
If running behind a firewall, verify that outbound connections to the API endpoint are allowed. Check your network configuration and firewall rules.
## Support
For issues or questions:
* GitHub Issues: [github.com/getsynq/synq-google-cloud-storage/issues](https://github.com/getsynq/synq-google-cloud-storage/issues)
* Coalesce Quality Support: [support](/support/support)
# Google Cloud Pub/Sub
Source: https://docs.synq.io/gcp-integrations/pubsub
Automatically discover and track Pub/Sub topics and subscriptions in Coalesce Quality
## Overview
The Coalesce Quality Google Cloud Pub/Sub integration automatically discovers all Pub/Sub topics and subscriptions in your Google Cloud project and creates custom entities for centralized asset management, governance, and cross-platform data lineage.
This integration can also serve as a reference implementation for building custom integrations using the [Coalesce Quality Public API](/api-reference/getting-started).
**Source code:** [github.com/getsynq/synq-google-cloud-pubsub](https://github.com/getsynq/synq-google-cloud-pubsub)
## What It Does
The integration:
* **Discovers** all Pub/Sub topics and subscriptions in your GCP project
* **Creates** custom entities for each resource
* **Tracks relationships** between topics and their subscriptions (optional)
* **Automatically cleans up** entities when resources are deleted
* **Supports flexible filtering** with include/exclude patterns
## Use Cases
* **Data Catalog Management**: Track all messaging infrastructure in one place
* **Resource Discovery**: Automatically maintain an up-to-date inventory
* **Compliance Tracking**: Monitor messaging configurations and subscriptions
* **Cross-Platform Lineage**: Connect Pub/Sub topics with data pipelines, BigQuery tables, and other systems
## Installation
### Download Pre-built Binaries
Download the latest release for your platform from the [releases page](https://github.com/getsynq/synq-google-cloud-pubsub/releases).
**Intel:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-pubsub/releases/latest/download/synq-google-cloud-pubsub_darwin_amd64.tar.gz
tar -xzf synq-google-cloud-pubsub_darwin_amd64.tar.gz
sudo mv synq-google-cloud-pubsub /usr/local/bin/
```
**Apple Silicon:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-pubsub/releases/latest/download/synq-google-cloud-pubsub_darwin_arm64.tar.gz
tar -xzf synq-google-cloud-pubsub_darwin_arm64.tar.gz
sudo mv synq-google-cloud-pubsub /usr/local/bin/
```
**AMD64:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-pubsub/releases/latest/download/synq-google-cloud-pubsub_linux_amd64.tar.gz
tar -xzf synq-google-cloud-pubsub_linux_amd64.tar.gz
sudo mv synq-google-cloud-pubsub /usr/local/bin/
```
**ARM64:**
```bash theme={null}
curl -LO https://github.com/getsynq/synq-google-cloud-pubsub/releases/latest/download/synq-google-cloud-pubsub_linux_arm64.tar.gz
tar -xzf synq-google-cloud-pubsub_linux_arm64.tar.gz
sudo mv synq-google-cloud-pubsub /usr/local/bin/
```
Download the `.zip` file from the [releases page](https://github.com/getsynq/synq-google-cloud-pubsub/releases) and extract it.
Requires Go 1.24 or later:
```bash theme={null}
git clone https://github.com/getsynq/synq-google-cloud-pubsub.git
cd synq-google-cloud-pubsub
go build
```
## Configuration
### Required: API Credentials
Create a `.env` file with your Coalesce Quality API credentials:
```bash theme={null}
SYNQ_CLIENT_ID=your_client_id_here
SYNQ_CLIENT_SECRET=your_client_secret_here
# GCP Project ID (optional if running on GCP or using gcloud)
GCP_PROJECT_ID=your-gcp-project-id
```
The GCP project ID can be auto-detected from:
* Environment variables (`GCP_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`, `GCLOUD_PROJECT`)
* `gcloud` CLI configuration
* GCP metadata server (when running on GCP)
### Optional: Advanced Configuration
For advanced customization, create a `config.yaml` file:
```yaml theme={null}
# Coalesce Quality API Configuration (defaults shown for EU region)
synq:
endpoint: "developer.synq.io:443" # EU region (default)
# For US region, use: "api.us.synq.io:443"; for AU, "api.au.synq.io:443"
oauth_url: "https://developer.synq.io/oauth2/token" # EU region (default)
# For US region, use: "https://api.us.synq.io/oauth2/token"; for AU, "https://api.au.synq.io/oauth2/token"
# GCP Configuration
gcp:
user_agent: "synq-pubsub-client-v1.0.0"
# project_id: "your-project-id" # Alternative to GCP_PROJECT_ID env var
# entity_group_id: "pubsub::custom-group-id" # Defaults to pubsub::
# Custom Entity Type IDs
types:
topic_type_id: 20
subscription_type_id: 21
# topic_icon: "path/to/custom-topic-icon.svg"
# subscription_icon: "path/to/custom-subscription-icon.svg"
# Resource Filters
filter:
topics:
include: [] # Empty means include all
exclude: [] # Regex patterns to exclude
subscriptions:
include: [] # Empty means include all
exclude:
# Exclude auto-generated per-pod subscriptions
- '-[a-z0-9]{9,10}-[a-z0-9]{5}\.subscription$'
# Relationship Management (disabled by default)
relationships:
enabled: false # Set to true to enable topic->subscription relationships
filter:
include: [] # Empty means include all (format: "topic_id->subscription_id")
exclude: [] # Regex patterns to exclude relationship pairs
# Examples:
# include: ["important-topic->.*"] # Only relationships for important-topic
# exclude: ["test-.*->.*"] # Skip relationships for test topics
```
**Configuration precedence:** defaults → config.yaml → environment variables → CLI flags
## Network Requirements
If your GCP project has firewall rules that restrict outbound connections, whitelist the Coalesce Quality egress IP addresses:
* App: [https://app.synq.io](https://app.synq.io)
* API: [https://developer.synq.io](https://developer.synq.io)
* **Egress IP: `34.105.135.39`**
* App: [https://app.us.synq.io](https://app.us.synq.io)
* API: [https://api.us.synq.io](https://api.us.synq.io)
* **Egress IP: `35.238.250.82`**
* App: [https://app.au.synq.io](https://app.au.synq.io)
* API: [https://api.au.synq.io](https://api.au.synq.io)
* **Egress IP: `35.189.4.22`**
See [IP Addresses](/security/ip) for the latest information.
## Running the Integration
### Basic Usage
```bash theme={null}
# Minimal setup: just create .env with credentials
cp .env.example .env
# Edit .env and add your credentials
# Run with defaults
./synq-google-cloud-pubsub
# Run with debug logging
LOG_LEVEL=DEBUG ./synq-google-cloud-pubsub
# Run with JSON logging for production
LOG_FORMAT=json ./synq-google-cloud-pubsub
```
### Enabling Relationships
To track relationships between topics and subscriptions:
```bash theme={null}
# Enable relationships via command-line
./synq-google-cloud-pubsub --relationships.enabled
# Or configure in config.yaml
# relationships:
# enabled: true
```
Relationships are **disabled by default**. Enable them only if you need to track topic-to-subscription connections in your data lineage.
### Dry-Run Mode
Preview what entities would be created without making API calls:
```bash theme={null}
# Dry-run mode (no API calls)
./synq-google-cloud-pubsub --dry-run
# Dry-run with debug logging and relationships
LOG_LEVEL=DEBUG ./synq-google-cloud-pubsub --dry-run --relationships.enabled
```
In dry-run mode, the integration scans Pub/Sub resources and shows what would be created, but does not call the API or require credentials.
### Command-Line Options
Common flags:
* `--dry-run` - Preview changes without calling the API
* `--gcp.project-id` - Specify GCP project ID
* `--relationships.enabled` - Enable topic-to-subscription relationships
* `--filter.topics.include` - Topic name patterns to include
* `--filter.topics.exclude` - Topic name patterns to exclude
* `--filter.subscriptions.include` - Subscription name patterns to include
* `--filter.subscriptions.exclude` - Subscription name patterns to exclude
* `--types.topic-type-id` - Custom entity type ID for topics (default: 20)
* `--types.subscription-type-id` - Custom entity type ID for subscriptions (default: 21)
* `--synq.endpoint` - API endpoint for your region
For US region:
```bash theme={null}
./synq-google-cloud-pubsub \
--synq.endpoint=api.us.synq.io:443 \
--synq.oauth-url=https://api.us.synq.io/oauth2/token
```
For AU region:
```bash theme={null}
./synq-google-cloud-pubsub \
--synq.endpoint=api.au.synq.io:443 \
--synq.oauth-url=https://api.au.synq.io/oauth2/token
```
Run `./synq-google-cloud-pubsub --help` for all available options.
## How It Works
1. **Authenticates** with the Coalesce Quality API using OAuth2 client credentials
2. **Creates/updates** custom entity types for topics and subscriptions
3. **Discovers** all Pub/Sub topics and subscriptions in your project
4. **Creates entities** for each resource
5. **Manages relationships** between topics and subscriptions (if enabled)
6. **Uses entity groups** to enable automatic cleanup of deleted resources
### Entity Groups
The integration uses entity groups to track all entities created in each run. When the group is updated, Coalesce Quality automatically removes entities that were in the previous group but not in the current one, enabling automatic cleanup of deleted resources.
### Relationship Management
When enabled, the integration creates relationships between topics and their subscriptions. The system:
* Deduplicates relationships to avoid recreating existing ones
* Cleans up relationships that no longer exist
* Supports filtering with include/exclude patterns
### Custom Identifiers
All entities use custom identifiers with `pubsub::` prefix for namespace isolation. Subscriptions use composite identifiers: `pubsub::::`.
## Scheduling with Cron
To keep your Pub/Sub inventory up-to-date, schedule the integration to run periodically:
```bash theme={null}
# Run every hour
0 * * * * cd /path/to/integration && ./synq-google-cloud-pubsub
# Run every 6 hours with relationships
0 */6 * * * cd /path/to/integration && ./synq-google-cloud-pubsub --relationships.enabled
```
## Using as a Public API Example
This integration demonstrates best practices for using the Coalesce Quality Public API:
* OAuth2 authentication with client credentials
* Creating and managing multiple custom entity types
* Using entity groups for automatic cleanup
* Managing relationships between entities
* Implementing filtering and dry-run modes
* Handling regional deployments
See the [source code](https://github.com/getsynq/synq-google-cloud-pubsub) and [API documentation](/api-reference/getting-started) for more details.
## Troubleshooting
Verify your credentials are correct in the `.env` file. Ensure you're using the correct endpoint for your region (EU or US).
Set the `GCP_PROJECT_ID` environment variable explicitly or configure `gcloud` CLI with your project:
```bash theme={null}
gcloud config set project YOUR_PROJECT
```
Ensure your GCP credentials have the following permissions:
* `pubsub.topics.list`
* `pubsub.subscriptions.list`
Use the default filter to exclude auto-generated per-pod subscriptions, or customize the filter patterns:
```yaml theme={null}
filter:
subscriptions:
exclude:
- '-[a-z0-9]{9,10}-[a-z0-9]{5}\.subscription$'
- 'test-.*'
```
If running behind a firewall, verify that outbound connections to the API endpoint are allowed. Check your network configuration and firewall rules.
## Support
For issues or questions:
* GitHub Issues: [github.com/getsynq/synq-google-cloud-pubsub/issues](https://github.com/getsynq/synq-google-cloud-pubsub/issues)
* Coalesce Quality Support: [support](/support/support)
# Incidents overview
Source: https://docs.synq.io/incidents/incident-overview
Incident management helps you manage the issue resolution from start to finish and avoid anything getting lost when the stakes are high.
Incident management is a helpful way for your entire team to stay on top of important issues and collaborate on resolving them. Incidents can be declared across issues monitored by Coalesce Quality, such as anomaly monitors, transformation layer tests (dbt, SQLMesh, Coalesce Transform), and failing Airflow tasks.
Benefits of declaring incidents
* Collaborate with your team on resolving issues keeping comments and status changes in one overview.
* Track and monitor ongoing incidents and who’s working on them.
* Review past incidents.
## Open incidents
The platform gives an [overview](https://app.synq.io/incident/open) of open, closed, and untriaged incidents to easily track what’s being worked on and by whom.
## Declaring an incident
Incidents can be declared through Slack alerts or directly in the UI. When you declare an incident, name it so it's easy for everyone to understand (*"duplication error from prod services impacting the ad-bidding ML model"* is better than *"issue with transformation model"*).
*Declaring an incident from a Slack alert*
*Declaring an incident from the UI*
### When to declare an incident?
There’s no one-fit-all solution for what makes an incident. We’ve seen two approaches work well.
1. For teams with few issues, all issues are declared as incidents
2. For teams with many ongoing issues, only P1 issues are declared as incidents (read more about how to assess the [cost of incidents here](https://www.synq.io/blog/cost-of-data-incidents))
## Prioritising an incident
Not all issues are equal. Some require that you drop everything and fix it, while others can wait until the end of the week. Use the impact assessment to get an overview, including `affected products`, `downstream products`, `downstream owners`, and `affected & downstream assets`. Clicking any group will show you the specific assets that are impacted.
You can also use the in-built lineage to narrow down impacted assets. For example, use the column-level lineage filter to narrow down the range of impacted assets.
## Collaborating on issues
To indicate you’re working on an issue within an incident, change the status to `Investigating`. This lets everyone else see you’re on it. The activity overview shows how the status of the incident has changed over its lifetime. This is particularly helpful when an incident spans multiple issues, as not everyone may work on the same things.
Use the comment section to make updates as you progress on investigating the incident.
If you use Slack, you can add a comment about the internal incident channel (e.g., #inc-user-stats-12-03-2024). This makes it easier to keep communication in one place and helps other people get up to speed if they return to the incident without having all the context.
## Closing or canceling an incident
Incidents self-resolve if the issue on the underlying asset is solved. This is visible in the UI, with the following banner on the incident. You still have to close the incident to move it to *closed incidents*.
You can close or cancel an incident using the Close incident button in the top right corner or Cancel incident behind the three dots. You should close the incident once it’s been resolved, and you want to keep track of it and use cancel in situations where the issue didn’t justify an incident.
## Reviewing past incidents
The [Past Incidents screen](https://app.synq.io/incident/past-incidents) lets you see all past incidents and filter by a date range. This is helpful if you want an overview of the number of incidents you declared in a time frame or if you’re looking back to investigate what happened at a specific time.
## Managing incidents programmatically
Declaring an incident, moving it through its states and commenting on it are all
available outside the app, so an incident opened by an on-call script or an AI
assistant is the same incident the team sees.
* **Public API** — `synq.incidents.v1.IncidentsService` covers incidents and
`synq.issues.v2.IssuesService` the issues attached to them. Reading needs a
token with `SCOPE_INCIDENTS_READ` and `SCOPE_ISSUES_READ`; writing needs
`SCOPE_INCIDENTS_EDIT` and `SCOPE_ISSUES_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [issue and incident tools](/scout/mcp#issue-and-incident-management)
read open issues and incidents, their impact and their history, and the
[write actions](/scout/mcp#write-actions) declare an incident over a set of
issues, edit it, set an issue's status and post comments. Reading is covered by
read-only consent; the writes need the Write permission and are attributed to
you with a **via MCP** marker.
## Bringing incidents across the platform
We’re continuously integrating incidents into core platform workflows. One example is showing ongoing incidents for data products so everyone can immediately see that their data may be impacted.
# Introduction
Source: https://docs.synq.io/introduction
How to set up and get the most out of Coalesce Quality
Coalesce Quality is a data observability platform designed for model and metric-centric data stacks with a strong emphasis on data products and ownership. It’s built for teams that power business-critical use cases to detect issues in near real-time without needing a full-time data engineer to manage and set up the platform.
The platform is simple on the surface, but complex under the hood. This means it’s simple to set up and get going—most customers are up and running in less than 30 minutes. But simultaneously, it’s powerful and flexible enough to support advanced use cases.
### Getting started
After you’ve been assigned a workspace, follow these steps for the optimal setup.
1. [Set up the relevant integrations](https://app.synq.io/settings/integrations)
2. [Create your first data product](/data-products/creating-a-data-product)
3. [Set up monitors](/monitors/setting-up-monitors)
4. [Invite your team members](/admin/invite_user)
5. [Set up owners and alerts](/ownership/creating-an-owner)
### Using the platform
Group related data assets to build reliability with end-end monitoring and ownership.
Proactively detect data issues with self-learning and powerful anomaly monitors.
Declare, track, and resolve incidents to stay on top of all business-critical issues.
Clarify ownership of data assets and activate workflows that help owners stay informed about the health and status of their data.
Notify impacted owners by tagging or routing highly configurable Slack alerts or sending them directly to the inbox.
Performant and automated lineage that spans your data warehouse and BI tool at a column level.
### Not yet using Coalesce Quality?
Read some of our [customer stories](https://www.synq.io/customers) or book a demo at [synq.io](https://www.synq.io)
# Column-level lineage
Source: https://docs.synq.io/lineage/column-level-lineage
## Supported sources
Column-level lineage is supported in all supported data warehouses and relational databases. In addition to that it also works down to Looker and Tableau.
## Visibility into issues
There are two possible when column-level lineage might not be accurate and we clearly indicate that.
### Parsing of SQL failed
In case our parser is unable to understand the SQL query, we indicate that both on the `Code & Changes` of an asset and on the lineage graph.
### Extraction of dependencies failed
In case when SQL contains edge case constructs, we might not correctly resolve how data in the columns flows. To prevent such issues, we do post-validation of our SQL understanding and expose those validation results both in `Code & Changes` tab of an asset and on the lineage graph.
## Limitations
We do our best to improve support for all different syntaxes and constructs available in different SQL dialects. If any feature needed is not supported please report that to us.
### BigQuery `STRUCT` literal lineage
We track lineage of individual `STRUCT` fields and `RECORD` column itself, so lineage will work when using them in CTEs and record simplified lineage as part of single SQL. When going from one asset to another we expose only information about columns and not the `RECORD` fields.
Consider this example:
```sql theme={null}
WITH c AS (
SELECT STRUCT (c1 AS a, c2 AS b) AS field FROM `prj`.`ds`.`tbl`),
d AS (SELECT STRUCT (field.a as d) double_nested
FROM c)
SELECT double_nested.d
FROM d
```
In this case we will correctly track simplified lineage from output column `d` to column `c1` in `prj`.`ds`.`tbl`.
When we have two assets, one defined as
```sql theme={null}
CREATE VIEW ds.rec_tbl(r) AS
SELECT STRUCT (c1 AS a, c2 AS b) AS r FROM `prj`.`ds`.`tbl`)
```
Second defined by query
```sql theme={null}
SELECT r.c1 AS r FROM `prj`.`ds`.`rec_tbl`
```
We show that column `r` depends on both `c1` and `c2`. We might consider changing that behaviour later.
# Lineage overview
Source: https://docs.synq.io/lineage/lineage
Our lineage is built to be performant and work out of the box. This means you’ll automatically get column-level lineage from BigQuery, Snowflake, Redshift, and ClickHouse to Looker and Tableau with the ability to explore thousands of assets with sub-second latency. The lineage is helpful for core workflows such as
* Understand the downstream impact of an issue across your transformation layers (dbt, SQLMesh, Coalesce Transform) and BI tools to decide the urgency at which you resolve it.
* Understand the downstream implications of making a change to a column upstream.
* Debugging a data issue to find upstream dependencies.
The lineage is automatically extracted from your data warehouse and BI tool several times daily, ensuring you always have an up-to-date view.
### Understanding the downstream impact of an issue
1. Alerts show the number of impacted assets. Clicking on the impacted assets brings you to the lineage view with these assets pre-filtered
2. Use the sidebar to quickly filter down to specific assets downstream, such as a Looker dashboard.
### Assessing the impact of making a change
1. Navigate to the lineage for the table you’re about to change and click on the table name in the lineage to toggle `Filter by column`
2. Filtering by a column lets you see all downstream assets where the column is being used. Click `X` to remove the column selection or `+` to expand the lineage.
3. Click on a column in one of the downstream assets to see how the column was derived in code
### Reading lineage programmatically
The graph behind the lineage view is available outside the app, so an impact
assessment can run in a CI check or a chat window rather than a browser tab.
* **Public API** — `synq.entities.lineage.v1.LineageService` traverses
dependencies in either direction, and `synq.entities.impact.v1.ImpactService`
answers what an issue on an asset would affect. Both need a token with
`SCOPE_LINEAGE_READ`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [lineage tools](/scout/mcp#data-lineage-analysis) walk upstream
and downstream to any depth, trace back to original sources, and find the
dependencies two assets share; the
[impact tools](/scout/mcp#impact-analysis) return the assets and systems an
issue reaches. All read-only, and covered by read-only consent.
### Upstream lineage to trace down the issue's root cause
1. Navigate to the most downstream asset, such as a Looker dashboard you’re debugging.
2. To trace upstream issues, use the left side menu bar to filter down on, e.g., sources or transformation models with status errors.
3. Click the data asset and select `Filter by column` to trace where a specific column is used upstream.
# Custom SQL monitor
Source: https://docs.synq.io/monitor-types/custom-sql-monitor
Custom SQL monitors are flexible monitors that let you express the SQL of your choice and automatically learn a time series around it. For example
* Ensure the accuracy of a key ML input numeric feature by creating a monitor to track the feature average over time
* Set up a monitor on average revenue grouped by geography to learn about sudden increases or drops for a specific region
* Detect unexpected patterns in the number of unique users created at the source level to prevent downstream impact
*A custom SQL monitor detecting an anomaly in the average sales price for one region*
### Setting up a custom SQL monitor
1. Navigate to the table or transformation model (dbt model, SQLMesh model, Coalesce node) where you want to set up the monitor and click `Add Monitor`
2. Select `Custom SQL` to start creating a custom SQL monitor
3. Configure the monitor with the relevant settings
* **SQL metric**—any SQL expression that specifies the metric to track (e.g., `avg(revenue)`). *This must be an expression that results in a metric*
* **Time partition**—the time partition you want your data segmented by (e.g., creation\_date)
* **Days to delay**—days to ignore if you have a delay in when you expect fresh data
* **Sensitivity**-see [Configuring sensitivity](/monitors/monitor-sensitivity)
* **Custom filter**-specify filters using SQL
4. Select a `variable` to segment by (*optional*). This will create multiple time series for each segment you select to include
* To create a monitor for specific segments, toggle `Selected values` and select the desired values.
* If you select `All values`, new values are automatically added.
Be cautious of including segments with only a few data points, as the model may not be able to confidently learn a reliable confidence interval for these segments, which can lead to many false/positive alerts
5. Click `continue` to set up the monitor with the specified configurations
The monitor will appear shortly and can be found directly under the table you created it for and on the [Health page](https://app.synq.io/healthz/overview)
Custom monitors partition your data into time intervals based on the timestamp value of a selected column. It will construct a historical time series, with each time partition representing one data point. Monitor bootstraps on the past 30 days and then continue to query only the most recent time partition (to minimize query cost). This means the monitor can detect anomalies right after setting it up.
**Resetting a monitor**
If you want to rerun predictions for the past 30 days in case you made historical updates on the data, you can go to the monitor, click `Settings`, and press `Reset monitor`. This step is not reversible to only proceed if you're certain you want to reset the monitor.
# Field stats monitor
Source: https://docs.synq.io/monitor-types/fieldstats
Field stats monitors automatically track key statistical properties of one or more fields in a table and learn a time-series pattern for each metric. These monitors help you detect unexpected changes in distribution, null rates, or value ranges without writing any SQL.
Examples of what you can detect with field stats monitors:
* Sudden spikes in null or empty values after a pipeline change
* Drops in the number of unique IDs, indicating upstream duplication issues
* Unusual shifts in a numeric metric such as fare amount, order value, lead time, etc.
* Outliers in mean, max, or standard deviation that may indicate data corruption
### Metrics tracked automatically
By default, each selected field generates a time-series for every metric available for its data type. Metrics can be narrowed per column; for monitors defined as code, see [monitors as code](/monitors/monitors-as-code).
| Metric | ID | Description |
| - | - | - |
| **Number of rows** | `NUM_ROWS` | Total number of rows in the dataset |
| **Number of null** | `NUM_NULL` | Count of null values |
| **Percentage of Null** | `PCT_NULL` | Percentage of null values |
| **Number of empty** | `NUM_EMPTY` | Count of empty string values |
| **Percentage of Empty** | `PCT_EMPTY` | Percentage of empty string values |
| **Number of unique** | `NUM_UNIQUE` | Count of unique values |
| **Percentage of Unique** | `PCT_UNIQUE` | Percentage of unique values relative to total rows |
| **Mean** | `MEAN` | Average value of the field |
| **Median** | `MEDIAN` | Median value |
| **Min** | `MIN` | Minimum value |
| **Max** | `MAX` | Maximum value |
| **Standard deviation** | `STDDEV` | Statistical standard deviation |
Metrics available per data type:
| Field data type | Metrics |
| - | - |
| String | `NUM_ROWS`, `NUM_UNIQUE`, `NUM_EMPTY`, `NUM_NULL`, `PCT_NULL`, `PCT_UNIQUE`, `PCT_EMPTY` |
| Numeric, integer, float | the string metrics plus `MEAN`, `MIN`, `MAX`, `MEDIAN`, `STDDEV` |
| Date, datetime | `NUM_ROWS`, `NUM_NULL`, `NUM_UNIQUE`, `MIN`, `MAX`, `PCT_NULL`, `PCT_UNIQUE` |
Redshift does not compute `NUM_UNIQUE` or `PCT_UNIQUE` on numeric columns, so they are skipped there. A column whose whole selection is skipped produces no predictions.
### Setting up a field stats monitor
1. Navigate to the table where you want to create the monitor and click `Add Monitor`
2. Select `Field stats` and choose the fields you want to monitor\
You can monitor one or multiple fields at once. For each selected field, choose the metrics to track; all are on by default.
3. Configure time partitioning. A time-series will be learned per metric per field, using the timestamp column you select.
4. Set sensitivity, and severity (optional)
Once confirmed, the monitor will appear under the table and on the [Health page](https://app.synq.io/healthz/overview)
Field stats monitors bootstrap the past 30 days of history, then only query new partitions going forward to reduce cost. Anomaly detection begins immediately after setup.
### Resetting a monitor
If you modify historical data and want to re-learn the 30-day baseline, open the monitor, go to `Settings`, and click `Reset monitor`.
This action is not reversible — only reset if you are certain the historical values have changed.
# Freshness monitor
Source: https://docs.synq.io/monitor-types/freshness-monitor
The default freshness and volume monitors have been merged into the Table stats monitor. You can see more on the [Table stats monitor page](/monitor-types/table-stats-monitor).
The freshness monitor looks at a table's timestamp and alerts you if it’s been too long since the last update.
Freshness monitors rely on your data warehouse metadata tables for timestamp information (e.g., Snowflake table [information schema](https://docs.snowflake.com/en/sql-reference/info-schema/tables)). As the monitor queries the information schema, you only incur minimal additional costs even when they are run frequently. The monitor works out of the box, which makes it easy to deploy at scale.
In most cases, we recommend combining freshness monitors with default [Volume monitor](/monitor-types/volume-monitor#default-volume-monitor) to detect when data is flowing but at a reduced rate. Read more about best practices for deploying these at [Setting up monitors](/monitors/setting-up-monitors)
Default freshness monitors are run every 60 minutes.
### Setting up a freshness monitor
1. Navigate to `Health` → `Manage monitors`
2. Click `Create monitor group` to define the tables you want to monitor
3. Use the browser to narrow down the tables you want to monitor
* **Browser**—select specific schemas or search for tables to monitor (your data warehouse tables and transformation models from dbt, SQLMesh, and Coalesce Transform are automatically mapped)
* **Annotation**—select assets with metadata definitions such as tags defined in your transformation layer (dbt, SQLMesh, or Coalesce Transform)
* **Important**—select assets that you’ve marked as important
* **Query**—advanced selection. E.g., search for specific keyword matches
To deploy freshness monitors on all sources automatically, filter by `Type: Source` (works with dbt sources, Coalesce sources, and SQLMesh external models)
4. Check `Freshness` to set up a freshness monitor group
5. Name the monitor (e.g., *freshness on all sources*).
6. Click `continue` to set up the monitor
You’ll be able to see the monitors on the [Health overview page](https://app.synq.io/healthz/overview) and adjust the monitor sensitivity.
As soon as you’ve set the monitor up, it will start learning the behaviors of your data every hour. You should expect \~10 days before the monitor has built an understanding of the seasonality and patterns in your data and is confidently able to predict anomalies
# Schema monitor
Source: https://docs.synq.io/monitor-types/schema-monitor
Schema changes and SQL definition changes are tracked automatically for every warehouse table and view. Set up a schema change alert to be notified about them.
The schema monitor tracks the columns of your warehouse tables and views and records every change to them. It works differently from the other monitor types:
* **There is nothing to set up.** Schema changes are tracked for every table and view in your connected data warehouses. That is why `Schema` is not offered under **Add monitor** on an asset, and it does not count toward your monitor limit.
* **It never notifies anyone on its own.** Changes are recorded on the asset, and you are notified only when a [schema change alert](#getting-notified-about-schema-changes) covers that asset.
* **It never raises an issue.** A schema change is a record of what changed, not a failure, so it does not appear in **Issues** and does not change an asset's health.
## What is detected
Each time Coalesce Quality refreshes the catalog of your data warehouse (every four hours by default), it compares the columns of every table and view with the previous refresh and records:
* **Column added**
* **Column removed**
* **Column type changed**, with the previous and the current type
Columns are matched by name, so a renamed column is recorded as one column removed and one column added. The first refresh of a new table only records its columns as the starting point, so a newly created table never shows up as a change.
Tables built by dbt, SQLMesh or Coalesce Transform are covered through the warehouse table or view they build, and the model's own page shows those changes too.
### SQL definition changes
Coalesce Quality also records when the SQL that defines an asset changes, for example the definition of a view, and keeps a diff of the old and new SQL. SQL definition changes are only recorded. They do not trigger alerts.
## Where to see schema changes
Open the asset in the catalog and select the **Changes** tab. It lists the commits, schema changes and SQL definition changes on the asset for the last 30 days by default. Use the **Type** filter to narrow it to **Schema changes** or **SQL definition changes**, and the date range to look further back.
Select a change to open its details:
* **Schema change**—each changed column, marked **Add**, **Remove** or **Type change**, with its type. A type change shows the previous and the current type.
* **SQL definition change**—a diff of the previous and the current SQL.
Each change is also recorded as a run in the asset's **Logs** tab, under the run types **Schema Change** and **SQL Change**.
## Getting notified about schema changes
To be notified when a schema changes, create a schema change alert:
1. Go to **Settings → Alerts**, and under **Asset alerts** click **Add alert**.
2. Under **Alert type**, select **Schema change**, then select the assets the alert covers.
3. Choose the alert settings:
* **Notify about upstream changes**—also alert on schema changes in assets upstream of the ones you selected.
* **Schedule digest**—turn on **Enable daily digest** to get one summary per day at a time you choose (UTC), instead of one alert per change.
4. Choose where the alert is sent. Schema change alerts can go to Slack, Microsoft Teams and email.
The alert lists the columns that were added, removed, or changed type. It fires on every schema change on the selected assets, so narrow the asset selection if you only want alerts for some of them.
See [Setting up alerts](/alerts/setting-up-alerts) and [Routing alerts](/alerts/routing-alerts) for the other alert options.
## Schema changes in issue triage
When [Scout](/scout/scout) triages an issue, it checks for schema changes on the affected asset and on the assets upstream of it in the days before the issue started, because an upstream column change often explains a failing test or monitor. This works whether or not a schema change alert is set up.
# Table stats monitor
Source: https://docs.synq.io/monitor-types/table-stats-monitor
The table stats monitor is an all-in-one monitor to detect if data is not flowing or flowing at a reduced rate. This can help you be the first to know about issues without having to maintain manual thresholds for freshness or volume
* A source that’s entirely stopped sending data
* A source that’s sending significantly fewer/more rows
* A join that causes a spike in duplicate values
*A table stats monitor highlighting a row count anomaly that’s been marked as expected*
To best possibly be able to catch different types of issues, the table stats monitor looks at three different signals
* **Row count** — the count of rows in the dataset
* **Delay** — the delay since the last time data was loaded
* **Change delay** — the delay since the last data load when at least one row was loaded
By combining the three, you’ll be more likely to detect different types of issues. For example, if an analytics engineer makes a join that creates a set of duplicate rows, the row count monitor can catch that. If an upstream source stops writing new data, the change delay monitor can catch that.
Unlike manual freshness checks (e.g., dbt source freshness or Coalesce freshness tests), all table stats are self-learning and automatically adjust to the patterns of your data. This reduces the workload on your end on defining specific thresholds.
Table stats monitors rely on your data warehouse metadata tables, such as the `row_count` from the Snowflake table [information schema](https://docs.snowflake.com/en/sql-reference/info-schema/tables). As the monitor queries the information schema, you only incur minimal additional costs even when they are run frequently. The monitor works out of the box, which makes it easy to deploy at scale.
**Setting up a table stats monitor**
1. Navigate to `Health` → `Manage monitors`
2. Click `Create monitor group` to define the tables you want to monitor
3. Use the browser to narrow down the tables you want to monitor
* **Browser**—select specific schemas or search for tables to monitor (your data warehouse tables and transformation models from dbt, SQLMesh, and Coalesce Transform are automatically mapped)
* **Type**—data asset type such as dbt source, Coalesce source, or SQLMesh external model
* **Annotation**—select assets with metadata definitions such as tags defined in your transformation layer (dbt, SQLMesh, or Coalesce Transform)
* **Important**—select assets that you’ve marked as important
* **Product**—select a data product to place monitor on or upstream of
* **SynQL**—advanced selection. E.g., search for specific keyword matches
4. Check `Table Stats` to set up a table stats monitor group
5. Name the monitor (e.g., *important sources*).
6. Click `continue` to set up the monitor
You’ll be able to see all monitors on the [Health overview page](https://app.synq.io/healthz/overview) and adjust the monitor sensitivity.
Metadata from your tables is automatically stored, so the anomaly monitor will learn the expected thresholds as soon as you set it up. If you’re just getting started or creating a new table that we don’t yet have metadata for, you should expect \~10 days before the monitor has built an understanding of the seasonality and patterns in your data and is confidently able to detect anomalies
**How frequently do the monitors run**
The table stats monitor is run every 30 minutes by default
# Volume monitor
Source: https://docs.synq.io/monitor-types/volume-monitor
The volume monitor looks at the number of rows in a table to detect how much data is added or removed and alerts you of abnormal increases or decreases. Among others, this can help you identify.
* A sudden jump in duplicate rows due to a faulty join
* A decrease in a data source due to an upstream error
* A system failure caused no new records to be inserted
*A volume monitor highlighting a sudden drop in the row count*
There are two different kinds of volume monitors. (1) [Default volume monitor](/monitor-types/volume-monitor#default-volume-monitor) and (2) [custom volume monitor](/monitor-types/volume-monitor#custom-volume-monitor). Read more about best practices for deploying these at [Setting up monitors](/monitors/setting-up-monitors)
## Default volume monitor
The default freshness and volume monitors have been merged into the Table stats monitor. You can see more on the [Table stats monitor page](/monitor-types/table-stats-monitor).
Default volume monitors rely on your data warehouse metadata tables, such as the `row_count` from the Snowflake table [information schema](https://docs.snowflake.com/en/sql-reference/info-schema/tables). As the monitor queries the information schema, you only incur minimal additional costs even when they are run frequently. The monitor works out of the box, which makes it easy to deploy at scale.
Unless you need custom functionality, such as grouping by a different time field, we recommend you use default volume monitors.
Default volume monitors are run every 60 minutes.
### Setting up a default volume monitor
1. Navigate to `Health` → `Manage monitors`
2. Click `Create monitor group` to define the tables you want to monitor
3. Use the browser to narrow down the tables you want to monitor
* **Browser**—select specific schemas or search for tables to monitor (your data warehouse tables and transformation models from dbt, SQLMesh, and Coalesce Transform are automatically mapped)
* **Annotation**—select assets with metadata definitions such as tags defined in your transformation layer (dbt, SQLMesh, or Coalesce Transform)
* **Important**—select assets that you’ve marked as important
* **Query**—advanced selection. E.g., search for specific keyword matches
4. Check `Volume` to set up a volume monitor group
5. Name the monitor (e.g., *important tables volume monitor*).
6. Click `continue` to set up the monitor
You’ll be able to see all monitors on the [Health overview page](https://app.synq.io/healthz/overview) and adjust the monitor sensitivity.
As soon as you’ve set the monitor up, it will start learning the behaviors of your data every hour. You should expect \~10 days before the monitor has built an understanding of the seasonality and patterns in your data and is confidently able to detect anomalies
## Custom volume monitor
Unlike the default volume monitor, the custom volume monitor queries your raw data. This gives you more configuration options, such as specifying a time dimension or automatically grouping by a segment. However, it also means that each monitor must be configured individually and that you may incur additional costs from running it.
Custom volume monitors are designed as *growth monitors* meaning that they look at the growth or shrink in the data relative to the previous period as specified by the time partition. This makes them suitable for detecting subtle changes or if no new data starts arriving, even for a short period.
### Setting up a custom volume monitor
1. Navigate to the table or transformation model (dbt model, SQLMesh model, Coalesce node) where you want to set up the monitor and click `Add Monitor`
2. Select `Volume` to start creating a custom volume monitor
3. Configure the monitor with the relevant settings
* **Time partition**—the time partition you want your data segmented by (e.g., *creation\_date*)
* **Days to delay**—days to ignore if you have a delay in when you expect fresh data
* **Sensitivity**—see [Configuring model sensitivity](/monitors/monitor-sensitivity)
* **Custom filter**—specify custom SQL to filter by
4. Select a `variable` to segment by (*optional*). This will create multiple time series for each segment you select to include
* If you only want to create a monitor for specific segments, toggle `Selected values` and select the desired values
* If you select `All values`, new values are automatically added.
Be cautious of including segments with only a few data points, as the model may not be able to confidently learn a reliable confidence interval for these segments, which can lead to many false/positive alerts
5. Click `continue` to set up the monitor with the specified configurations
The monitor will appear shortly and can be found directly under the table you created it for and on the [Health page](https://app.synq.io/healthz/overview)
Custom monitors partition your data into time intervals based on the timestamp value of a selected column. It will construct a historical time series, each time partition representing one data point. Monitor bootstraps on the past 30 days and then continue to query only the most recent time partition (to minimize query cost). This means that the monitor will be able to detect anomalies right after you’ve set it up
**Resetting a monitor**
If you want to rerun predictions for the past 30 days in case you made historical updates on the data, you can go to the monitor, click Settings, and press Reset monitor. This step is not reversible to only proceed if you’re certain you want to reset the monitor.
# Agent workflow
Source: https://docs.synq.io/monitors/agent-workflow
The operating guide for a coding agent driving the synqcli CLI
This page is the published copy of the operating guide that ships with the tool
as `AGENTS.md` — in the release archive and in
[getsynq/synqcli](https://github.com/getsynq/synqcli). It is deliberately
denser than the rest of this section. Start at
[Monitors as code](/monitors/monitors-as-code) for what the YAML declares, and
see the [CLI reference](/monitors/cli) for every flag.
This is the operating guide for a coding agent using `synqcli` to declare data
quality monitors, SQL tests and deployment rules as code: the loop to follow, how
to read what it prints, the refusals and what each one actually means, and the
mistakes that cost real time. It is written to be acted on top-down.
**`synqcli deploy` reconciles a workspace with a YAML file.** You declare which
monitors and tests belong on which assets; `deploy` makes the workspace match.
That means it creates, it updates, and **it deletes** — anything the file's
namespace owns that the file no longer declares is removed. Understanding that one
sentence prevents most of the damage this tool can do.
Two more facts decide almost everything else, and both are covered in full below.
A check is addressed by its `id:` — write one and the rest of the check is
editable, leave it out and its content is its identity, so some edits replace the
check instead of updating it. And a check belongs to exactly one namespace, so
pointing a second config at it is refused rather than merged.
## 1. The loop
```bash theme={null}
synqcli auth status # 1. know which workspace you are about to change
synqcli export --source api existing.yaml # 2. see what is already declared
synqcli deploy config.yaml --dry-run # 3. read the plan, especially the deletes
synqcli deploy config.yaml # 4. apply, after the same plan is shown again
synqcli deploy config.yaml --dry-run # 5. confirm the plan is now empty
```
Step 5 is not ceremony. An empty plan is the only proof that the file and the
workspace agree, and therefore that the file is now the source of truth. Anything
else means the next person to run `deploy` gets changes they did not write.
**Get the schema into the editor first.** It is the field-level reference, and it
is authoritative in a way prose is not:
```bash theme={null}
synqcli schema > schema.json
```
Then start every config file with the line that binds it, which gives completion
and validation as you type:
```yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-monitors/v1/config.schema.json
```
The same schema is served at that URL, so the local copy is optional.
***
## 2. Entity ids: resolve them, never guess them
This is the most common failure, and the one the CLI can help with least. A wrong
id is not a typo it can correct — the asset simply is not there.
An entity id in the YAML is written in one of three forms, and all three are
resolved against the workspace before anything is deployed:
| Form | Example | Resolved by |
| - | - | - |
| Full path, `::` separated | `ch-prod::default::runs` | direct lookup |
| Full path, dot separated | `ch-prod.default.runs` | dots are converted to `::`, then looked up |
| Warehouse coordinates (SQL FQN) | `analytics.public.orders` | matched against known warehouse objects |
The first two are the same thing written two ways. The third is different: it is
what the table is called in the warehouse, and it is matched only for ids the
first lookup did not find.
**Resolution can only succeed for an asset the platform already knows about**, and
only for a table, a view or an equivalent model. An id that names the same asset
under another integration — a Coalesce node or a dbt model, for example — is
accepted and resolves to that warehouse table, or its view when there is no table,
so the file can carry either. When such an id is written as a full `::` path,
`deploy` prints the replacement as ` → `. Any other known entity that is
not a table or view — a dashboard, a job — is not monitorable, and fails with the
message below.
**Get real ids rather than constructing them.** Two reliable sources:
* `synqcli export --source all out.yaml` and read the `entities[].id` values it
writes. They are already in the simplified form the config accepts.
* The asset's page in the web app, which shows the same path.
### Reading the failure
```
The following entity IDs could not be resolved:
- ch-prod.default.runz
```
The id names nothing in this workspace. Either it is misspelled, the asset has not
been ingested yet, or — much more often than it looks — you are pointed at the
wrong workspace. Check `auth status` before you check the spelling.
```
The following entity IDs resolved to multiple entities:
- orders
- ch-prod::default::orders
- bq-prod::analytics::orders
Please specify more specific entity IDs to resolve to a single entity.
```
A short or coordinate-shaped id matched more than one asset. The fix is to replace
it with one of the full paths listed underneath — they are printed precisely so
they can be pasted back into the file. A Coalesce node or dbt model that builds
several views and no table is reported the same way.
```
coalesce-prod::env::node-1 (coalesce node) has no warehouse table or view to monitor
```
The id names a real asset, but nothing in the warehouse that a check can run
against — a node that has not been materialised, or an asset that is not a table at
all. Point the check at the table or view itself.
**An unresolved id fails the whole run**, including `--dry-run`, and no plan is
printed. Fix every id before expecting to see a plan at all.
***
## 3. The namespace is the reconcile boundary
```yaml theme={null}
namespace: "team-analytics"
```
`namespace` groups checks so that different teams reconcile independently. It is
the single most consequential field in the file:
* **Deleting is scoped to it.** `deploy` removes checks that this namespace owns
and the file no longer declares. It never touches another namespace's checks.
* **A missing `namespace` is itself a namespace** — the default one — not an
absence of grouping.
* **Getting it wrong is not a no-op.** A typo means "this namespace declares
nothing that it used to own", which is a request to delete, not a request to
skip.
* Several files may share one namespace. They are parsed together and reconciled
as one unit, so a plan for a namespace reflects every file that declares it.
`deploy --namespace ` filters which namespaces in the parsed files are acted
on; it is repeatable. It does not change what a namespace owns, only which of them
this run processes.
**`export --namespace` is a different thing entirely.** On `export`, `--namespace`
is the value *written into* the generated YAML — a label, not a filter. `export`
has no namespace filter, so it returns everything matching the other scopes and
stamps whichever namespace you named onto all of it. Exporting the whole workspace
under a namespace and deploying that file back is how one config comes to claim
checks that belonged to five others.
***
## 4. `--dry-run` and the confirmation prompt are not the same thing
They read as one thing and are not:
* `--dry-run` computes and prints the plan, then **exits without deploying**. It
never prompts. Nothing is written.
* Without `--dry-run`, the same plan is printed and then the run **asks**, per
namespace, whether to apply it. Answering anything other than `y` cancels that
namespace and moves to the next.
* `--auto-confirm` answers `y` for you. The plan is still printed; nothing is
reviewing it.
For an agent this matters twice over. **Without `--auto-confirm` a non-interactive
run blocks on the prompt** — it does not proceed, and it does not fail. And
`--auto-confirm` does not skip the review step so much as remove it, which is only
safe when a `--dry-run` of the same file was read first.
The order that is both non-blocking and reviewed:
```bash theme={null}
synqcli deploy config.yaml --dry-run # read this output — it is the review
synqcli deploy config.yaml --auto-confirm
```
`--dry-run` and `--auto-confirm` together are accepted and mean `--dry-run`:
nothing is deployed.
***
## 5. Authoring the config
A complete, minimal file:
```yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-monitors/v1/config.schema.json
version: v1beta2
namespace: "team-analytics"
defaults:
severity: ERROR
entities:
- id: ch-prod.default.orders
time_partitioning_column: created_at
monitors:
- id: orders_volume
type: volume
- id: orders_freshness
type: freshness
expression: created_at
tests:
- id: order_id_unique
type: unique
columns: [order_id]
- id: required_order_fields
type: not_null
columns: [customer_id, order_date]
- id: order_status_values
type: accepted_values
column: status
values: [pending, processing, shipped, delivered, cancelled]
```
Give every monitor and test an `id:` of your own, as above. It is optional, but it
is the check's address: with one, editing any other field updates the check and
keeps its history, and without one a good many edits replace it instead. Section 6
is the whole story.
`version: v1beta2` is the current format; `v1beta1` is legacy and exists for
compatibility only. Do not author new files in it.
**Monitor types** watch an asset over time and learn what normal looks like:
`volume`, `freshness`, `field_stats`, `custom_numeric`, `category_distribution`,
`table_stats`, `custom_table_stats`. A `field_stats` column can carry its own
metric selection, each metric with its own sensitivity.
**SQL test types** assert something is true right now: `not_null`, `empty`,
`unique`, `accepted_values`, `rejected_values`, `min_max`, `min_value`,
`max_value`, `freshness`, `relative_time`, `business_rule`, `business_query`,
`relationships`. The two that carry SQL you write yourself — `business_rule` and
`business_query` — accept `{{ table }}`, replaced with the anchored table's fully
qualified name. On a `sql_tests` deployment rule that is what makes one query
cover every matched table instead of being copied onto each of them; any other
`{{ name }}` token is rejected on save. See
SQL tests.
**Deployment rules** (`deployment_rules`, and `deployment_exclusions` to carve
assets back out) apply checks by *query* rather than by listing assets, so a new
matching asset is covered without editing the file. `type` says what a rule
deploys: `table_stats` deploys monitors, `sql_tests` deploys the SQL tests listed
under its own `tests:`, onto every table or view the selection matches. On a
`sql_tests` rule the schedule, timezone, severity and `save_failures` are
rule-level and govern every test it deploys; a test may override only `severity`.
A `sql_tests` rule deploys each test only onto the matched tables that have every
column it reads, and the deploy plan lists the rest as skipped tests. So a broad
selection is safe to write: a rule carrying three tests covers each table with
the ones that fit it, and the plan says where they landed.
`export` writes `sql_tests` rules back into the `deployment_rules` list, so a
workspace that has them can be adopted with the loop in section 1. A
`table_stats` rule is written as the single-asset `table_stats` monitors it
covers; its query form is authoring-only, so a file that has to keep one must
keep the hand-written version.
Where to look for the fields of any one of them, in order of authority:
1. [the published schema](https://schemas.synq.io/synq-monitors/v1/config.schema.json)
— every field, every accepted value.
2. `examples/` — one file per monitor type, per test type and per
pattern, all of them deployable as written.
3. SQL tests for the test types in prose, and
[Monitors as code](https://docs.synq.io/monitors/monitors-as-code) for what the
YAML declares and why.
An unknown key on an entity, a monitor or a test is an error, not something
ignored, so a misspelled field there fails the run rather than silently doing
nothing. Deeper inside a single test's own options that check thins out, so a
run that succeeds is not by itself proof that every key landed. Check the plan:
a field that was accepted shows up in the diff for an existing check.
***
## 6. What re-deploying changes, and what it replaces
`deploy` matches a check by its **id**, so what the id is derived from decides
whether an edit is an update or a replacement — and a replacement is not an edit.
The old check is deleted, with its issue history and its error runs, and for a
monitor with the baseline it has learned; a new one is created in its place.
**Write an `id:` of your own and it becomes the check's address:**
| `id:` | What identifies the check |
| - | - |
| a name of your own — `orders_nulls` | the namespace, the entity, the check type, and that name. Nothing else |
| a UUID | itself, verbatim — this is what `export` writes |
| omitted | the check's *content*, which differs per type — see the tables below |
So under an `id:` of your own, everything except the entity and the type is an
attribute you can edit in place: the `metric_aggregation` of a `custom_numeric`,
the column of a `category_distribution`, a test's columns or its SQL, the mode,
the segmentation, the partitioning column. Three things still replace the check, and each has a reason
that will not go away:
* **renaming the `id`** — it is the address, so a new one is a new check;
* **moving the check to another entity** — the API refuses to point an existing
monitor at a different asset;
* **changing its `type`** — a `volume` monitor that becomes a `freshness` monitor
measures something else, so neither its baseline nor its history means anything
under the new one.
`id:` is optional, and **without one the content is the identity** — which is what
lets two checks of the same type sit on one entity without either of them naming
an id. The cost is that editing a field in that content replaces the check. The
per-type content is: for a `custom_numeric` its `metric_aggregation`, for a
`category_distribution` its column, for a test its columns and, for a
`business_rule` or `business_query`, the SQL; plus, for every monitor, the mode,
the time-partitioning expression and the segmentation expression.
Two `field_stats` monitors on one entity are the exception: the content
derivation does not include their `columns`, so a pair that differs only there
gets the same id and the deploy fails on the duplicate. Give at least one of them
an `id:`.
So, for monitors:
| Edit | With an `id:` | Without one |
| - | - | - |
| `severity`, `sensitivity`, thresholds, `timezone`, schedule, `name`, `description`, `filter`, `category`, the `never_negative` of a `custom_numeric` | update in place | update in place |
| the `metric_aggregation` of a `custom_numeric` | update in place | replace |
| the column of a `category_distribution` | update in place | replace |
| `time_partitioning_column`, segmentation | update in place | replace |
| switch `anomaly_engine` ⇄ `fixed_thresholds` | update in place | replace |
| the `columns` of a `field_stats` | update in place | update in place |
| the metrics of a `field_stats` column | update in place | update in place |
| move the monitor to a different entity | replace | replace |
| change the monitor `type` | replace | replace |
| rename the `id` | replace | — |
And for tests:
| Edit | With an `id:` | Without one |
| - | - | - |
| `severity`, recurrence, evaluators, `name`, `description`, `save_failures`, `category` | update in place | update in place |
| the `values` of an `accepted_values`, the bounds of a `min_max` | update in place | update in place |
| the `column`/`columns` a test names | update in place | replace |
| the SQL of a `business_rule` or `business_query` | update in place | replace |
| move the test to a different entity | replace | replace |
| change the test `type` | replace | replace |
| rename the `id` | replace | — |
A deployment rule works the same way, and it matters more there than anywhere
else. **Deleting a `sql_tests` rule deletes every test the rule deployed**,
together with those tests' check entities and their history. Nothing brings
them back — not the tests, not their history.
Renaming a rule that carries no `id:` replaces it: the old rule is deleted
with everything it deployed, and the next scheduled sync builds the tests
again from scratch under the new name. A monitor rule is gentler — its
monitors survive as long as some rule still covers the asset — but the
rule's own id changes, so every link and every `id=` someone held goes
stale.
| `id:` on a rule | What identifies the rule |
| - | - |
| a name of your own | the namespace and that name, so the rule's `name:` becomes editable |
| a UUID | itself, verbatim — `export` writes one |
| omitted | the namespace and the rule's `name:`, so a rename replaces it |
That holds for both kinds of rule and for a `deployment_exclusions` entry.
Everything else — the `resolver_ql` it selects with, and whatever the kind carries
to describe the checks it deploys — is an attribute. The two kinds carry different
attributes, so the table names which fields belong to which; a field from the
other kind is rejected, not ignored.
| Edit | With an `id:` | Without one |
| - | - | - |
| `resolver_ql` — broaden, narrow, or just reformat | update in place | update in place |
| `severity` — either kind | update in place | update in place |
| `metrics`, `sensitivity`, `delay_model` — `table_stats` only | update in place | update in place |
| the `tests:` a rule deploys, plus `schedule`, `timezone`, `save_failures`, `keep_removed_tests` — `sql_tests` only | update in place | update in place |
| `name` | update in place | replace |
Updating a rule in place resyncs it: checks appear on assets the selection now
matches, go away from assets it no longer matches, and assets that match both
before and after keep the checks they already had, with their history.
One rule kind narrows the field: an entity-anchored `table_stats` monitor becomes
a rule on that one asset, and there is one such rule per asset — so it is
addressed by the asset, and its `id:` accepts only the UUID `export` writes for
it, adopting that rule. A name of your own there is rejected rather than
reinterpreted. Converting such a monitor into a query rule under
`deployment_rules` is a different rule selecting differently, not an edit: expect
a create and a delete.
A rule and an exclusion may share a name, or an `id:`: they are two different
things and each keeps its own identity.
**`--dry-run` names a monitor replacement**, on one line — `Replaced, id changed: → `, with what it discards and the fields that differ — and counts it as
neither a create nor a delete. Anything on one asset and of one type that leaves as
one monitor and arrives as one monitor is paired that way: a renamed `id:`, an edit
to a monitor carrying no `id:`, or two unrelated monitors that happen to share the
asset and the type. **Read the diff, not the word "Replaced"** — it is what says
which of the three you are looking at, and the line above prints the new monitor's
name, so a `name:` in the diff means the one going away had a different one. An
entity move and a `type:` change stay a delete plus a create in the two sections of
the plan, because both really are a different check. A replaced **test** or **rule**
is a delete plus a create too, so read those two sections when you have edited an
`id:` or a rule's `name:`.
"Update in place" above means the check keeps its id and its history. A few of
those updates keep the check but still discard what it has learned, which is the
next section.
***
## 7. Resets and re-runs: updates that are not free
Some in-place updates keep the check but disturb what it has. The plan flags
these. What they cost is not the same for a monitor and for a test, and the two
used to share one word.
**A monitor reset discards the learned baseline.** It has no anomaly baseline
until it has collected enough history again, and during that window it is not
really watching anything. A monitor resets when its timezone changes, its mode
changes (`anomaly_engine` ⇄ `fixed_thresholds`), its schedule changes (type, or
the time of day / minute of hour, or the delay), its `metric_aggregation` or a
`category_distribution`'s column or `top_k_limit` changes, its time-partitioning
expression or interval changes, or its segmentation changes.
The mode, the `metric_aggregation`, a `category_distribution`'s column, the
partitioning expression and the segmentation reach a reset only when the monitor
carries an `id:`. Without one they are part of its identity, so the same edit
replaces the monitor outright, which is strictly worse.
**A test re-run discards nothing.** A test has no learned state, so what the plan
calls out is that the test will run again: it is re-armed and runs immediately
rather than at its next scheduled slot. A test is re-armed when its recurrence,
its severity, its evaluators, or its template body change — the last one meaning
any test-specific field, including the columns it names, the `values` of an
`accepted_values` or the bounds of a `min_max`. A test with no `schedule:`, run by
something outside `synqcli`, is not re-armed at all; it runs at its next external
trigger.
What a test *does* lose is a replacement, from § 6: a new id means a new check
entity, so its issues and its error runs are gone. That is the thing an `id:`
buys, not the re-run.
There is no flag to suppress a reset and no way to deploy the change without it.
The choice is to accept it or to leave the field alone, so the useful thing an
agent can do is notice it in the plan and say so before confirming, rather than
discover it afterwards.
***
## 8. Refusals, and what each one means
### "managed by other configs" — the deploy is refused
```
🚫 2 monitors managed by other configs.
- Monitor ID: 7f3b…, Managed by namespace: team-platform
```
This is the ownership gate, and it is the only condition that refuses a deploy
outright. It means your file declares a check that already exists and belongs to a
different namespace. Two configs claiming one check would make every deploy fight
the other, so the tool stops instead of picking a winner.
**It aborts the entire run, not just that namespace**, and it aborts `--dry-run`
too — so no plan is printed for any namespace until it is resolved.
The recovery, in the order to try it:
1. **Confirm it is not just the wrong namespace on your side.** The message names
the namespace that owns the check. If that is the namespace your file should
have declared, fix `namespace` and the conflict disappears.
2. **Stop declaring it.** If it genuinely belongs to the other config, remove it
from yours. This is the right answer far more often than transferring it.
3. **Transfer it deliberately**, if it really should move: remove it from the
owning config and deploy that config first, then add it to yours and deploy
again. Between the two deploys the check does not exist — it is deleted and
recreated, so it loses its history. There is no in-place handover.
Deciding which config should own a check is a judgement about how a team works, so
it is one to put to whoever owns the config, not one to resolve by editing the
other team's file.
### "Managed by API (No Config)" and monitors created in the app — not refused
Both appear in the plan under their own headings, and neither blocks anything.
**They are adoptions:** a check created in the web app, or created over the API
without a namespace, is taken over by your namespace when your config declares it.
From then on it is yours, and a later deploy that stops declaring it will delete
it.
That is usually what was wanted, but it is silent and it is one-way. If a check was
deliberately maintained in the UI, declaring it in a config takes it away from
whoever maintains it there.
### Everything else that stops a run
| Refusal | What it means |
| - | - |
| An entity id could not be resolved | § 2 — the asset is unknown, or you are on the wrong workspace |
| An entity id resolved to multiple entities | § 2 — use one of the full paths it lists |
| An entity id has no warehouse table or view to monitor | § 2 — it names an asset that is not a table; use the table or view itself |
| Duplicate checks | Two entries in the file reduce to the same identity. Give them different columns, expressions or ids |
| A validation error | The message names the field. Check it against the schema |
| A field is rejected as unknown | Unknown keys are errors, not warnings. Usually a misspelling or a v1beta1 field in a v1beta2 file |
| A parse error in a file you did not mean to deploy | `deploy` with no arguments walks the working directory for every `.yaml` and `.yml`. Pass files explicitly |
All of these are collected across every file and namespace and reported together,
then the run exits without deploying anything. That is deliberate: fix them in one
pass rather than one at a time.
***
## 9. Running it non-interactively
```bash theme={null}
synqcli deploy config.yaml --dry-run
synqcli deploy config.yaml --auto-confirm
```
* **Credentials**, in precedence order: `--client-id` / `--client-secret` flags;
`QUALITY_CLIENT_ID` / `QUALITY_CLIENT_SECRET` (or a pre-issued `QUALITY_TOKEN`);
a `.env` file in the working directory; a cached browser login. Client
credentials beat a cached login, so CI is unaffected by whoever logged in last.
* **The deployment** is named, not spelled out: `--region eu|us|au`, or
`--endpoint` for a self-hosted one. `synqcli auth login` remembers the choice and
`synqcli auth use ` switches it, so later commands need no flag. **A
wrong region is the most expensive mistake available here** — it reconciles a
workspace that does not have your checks declared, and reconciling means
deleting.
* **Confirm the target before writing.** Every `deploy` and `export` prints the
workspace it connected to, before it does anything. Read that line.
* **An execution log** in JSONL is written when `--log-file` or `$QUALITY_LOG_FILE`
is set: one record per phase, per namespace result and per error. This is the
machine-readable version of the run, and it is much better to parse than the
formatted output.
* **Exit codes:** `0` means the command did what it said, `1` means it did not — a
failed deploy or export, and also a mistyped flag, an unknown command, a missing
argument or an unusable `--region` / `--endpoint`. So a CI step that checks the
status also catches an invocation an older `synqcli` does not understand, rather
than passing having deployed nothing.
* **A cancelled namespace is not a failure.** Declining the prompt exits `0`.
`--dry-run` also exits `0` whether or not there were changes. If a CI gate needs
"nothing to deploy", read the log file rather than the status.
***
## 10. `advisor` — propose tests for an asset
`advisor` asks a model to suggest tests for an entity and can write them straight
into a config:
```bash theme={null}
synqcli advisor \
--entity-id "ch-prod::default::orders" \
--instructions "Suggest data quality tests for this table" \
--output ./suggested
```
`--output` is a **directory**; one file is written per entity, and an existing file
is skipped unless `--force` is passed. `--entity-id` is repeatable. Other flags
worth knowing: `--columns` to narrow the scope, `--instructions-file` for a long
prompt, `--severity` for the default it assigns, `--namespace` for the group it
writes into, and `--connections` to let it profile the warehouse — which is what
lets it propose real `accepted_values` and real bounds instead of guesses.
**Review what it writes before deploying it.** `--output`, then read, then `deploy --dry-run` is the safe order. It proposes; it does not know what the workspace
already has.
Being specific in the instructions pays. Some that work:
```
This is an orders table. Suggest tests for:
- Order ID uniqueness
- Required fields: customer_id, order_date, total_amount
- Amount validation (total_amount >= 0)
- Date consistency (order_date <= ship_date)
- Status values should be: pending, processing, shipped, delivered, cancelled
```
```
This is financial transaction data. Suggest tests for:
- Transaction ID must be unique
- Amount must be non-negative
- Currency code should be valid ISO 4217
- Created timestamp must not be in the future
- Account IDs must not be null
```
```
This is time series data. Suggest tests for:
- Timestamp freshness, within the expected arrival interval
- Volume monitoring, to detect missing periods
- Metric value ranges
- No duplicate timestamps per entity
```
***
## 11. What each command costs
* `schema` — free and offline. No credentials, no API call.
* `auth status` — reads local files only. No network.
* `export` — reads the workspace. Cheap. Note that it **refuses to overwrite an
existing output file**, so give it a fresh path.
* `deploy --dry-run` — resolves every entity id and diffs against the workspace.
Cheap, and always worth it.
* `deploy` — writes. The cost is the blast radius, not the runtime: it creates,
updates and **deletes** in one pass.
* `advisor` — a model call per entity, plus warehouse metadata reads and, with
`--connections`, profiling queries against the warehouse itself. The only command
here with a per-run cost worth thinking about; scope it with `--entity-id` and
`--columns` rather than pointing it at everything.
* `upgrade` — replaces this binary with the latest release, verified against the
release's `checksums.txt`; `upgrade --check` reports what it would do and changes
nothing. Neither touches the workspace. `synqcli` also mentions a newer release
on stderr about once a day, and stays silent when an agent is driving it, when
output is not a terminal, in CI, and in a container — so it never lands in output
you are parsing. `QUALITY_NO_UPDATE_CHECK=1` turns it off outright.
***
## 12. Never do these
* **Never run `deploy` without reading a `--dry-run` first** on a workspace whose
config you did not author. Deploy is a reconcile, so an incomplete file is a
delete.
* **Never guess a `namespace`.** A typo does not scope the run down, it asks for a
deletion.
* **Never export the whole workspace under a namespace and deploy it back.**
`export --namespace` labels, it does not filter — see § 3.
* **Never invent a UUID for an `id:`.** A name of your own is the supported way to
address a check (§ 6); a UUID you made up addresses nothing and, on an
entity-anchored `table_stats` monitor, adopts a rule that does not exist. Copy a
UUID only from `export` or from the app.
* **Never assume a rename is a rename.** Check it against § 6 first, and read the
replacement line and the delete list in the plan.
* **Never self-confirm a monitor reset.** If the plan says a monitor resets, say so
and let the person decide — the baseline it discards is not recoverable. A test
that "will run again" discards nothing and needs no such pause.
* **Never point a config at a workspace you have not confirmed.** `auth status`,
and read the workspace line the command prints.
* **Never edit the schema or the CLI reference by hand.** Both are generated.
***
## 13. When something looks wrong
| Symptom | Cause |
| - | - |
| The plan deletes checks you did not expect | Wrong `namespace`, or a file missing what the namespace already owns — export first |
| The command hangs and prints nothing further | It is waiting on the confirmation prompt. Use `--dry-run`, or `--auto-confirm` |
| A check was recreated instead of updated | Its address changed — a renamed `id:`, another entity, another `type` — or it carries no `id:` and a field of its content changed. § 6 |
| A monitor updated but lost its baseline | A reset — § 7 |
| "managed by other configs" refuses everything | The ownership gate — § 8 |
| An entity id does not resolve | § 2, and check the workspace before the spelling |
| A check created in the app is now in your plan | Adoption, which is expected and one-way — § 8 |
| `deploy` complains about YAML you never wrote | With no file arguments it walks the working directory. Pass files explicitly |
| Nothing deploys and the status is still `0` | The prompt was declined, or `--dry-run` was set |
The full command and flag reference is generated from the CLI itself and published
as the [CLI reference](https://docs.synq.io/monitors/cli). For what the YAML
declares and why, start at
[Monitors as code](https://docs.synq.io/monitors/monitors-as-code); for the test
types in detail, [SQL tests](https://docs.synq.io/monitors/sql-tests) and
[Deployment rules](https://docs.synq.io/monitors/deployment-rules).
# Monitors CLI reference
Source: https://docs.synq.io/monitors/cli
Every synqcli command, argument and flag, generated from the CLI itself.
This page is generated from the `synqcli` command tree, so it always matches
the released binary. For what the YAML declares and how to structure it, see
[Defining monitors in code](/monitors/monitors-as-code); the field-by-field
format is the [configuration reference](https://schemas.synq.io/synq-monitors/v1/config.html).
Manage Coalesce Quality monitors, SQL tests and deployment rules as code.
A YAML configuration declares which monitors and tests belong on which assets.
The deploy command reconciles a workspace with that configuration, export writes
what a workspace already has back out as YAML, and advisor proposes tests for an
asset. The schema command writes the JSON schema out for editor completion.
## Global flags
These apply to every command.
| Flag | Type | Default | Description |
| - | - | - | - |
| `--client-id` | `string` | — | Coalesce Quality API client ID (overrides .env and environment variables) |
| `--client-secret` | `string` | — | Coalesce Quality API client secret (overrides .env and environment variables) |
| `--endpoint` | `string` | — | Coalesce Quality API endpoint, for staging or a self-hosted deployment; overrides --region (or \$QUALITY\_API\_ENDPOINT) |
| `--region` | `region` | — | Coalesce Quality deployment to use: au, eu, us (or \$QUALITY\_REGION) |
## Commands
### `synqcli advisor`
Suggest data quality tests for one or more entities
```bash theme={null}
synqcli advisor [flags]
```
The advisor command uses AI to suggest appropriate data quality tests and monitors
for one or more entities (tables) based on provided instructions.
Example (single entity, JSON output):
```bash theme={null}
synqcli advisor \
--entity-id "postgres::public::users" \
--instructions "Suggest basic data quality tests"
```
Example (single entity with YAML file output):
```bash theme={null}
synqcli advisor \
--entity-id "postgres::public::users" \
--instructions "Suggest basic data quality tests" \
--output ./tests
```
Example (multiple entities):
```bash theme={null}
synqcli advisor \
--entity-id "postgres::public::users" \
--entity-id "postgres::public::orders" \
--entity-id "postgres::public::products" \
--instructions "Suggest basic data quality tests" \
--output ./tests
```
Example (with DWH connection for data profiling):
```bash theme={null}
synqcli advisor \
--entity-id "postgres::public::users" \
--instructions "Suggest accepted_values tests for enum columns" \
--connections ./connections.yaml
```
Example (filter to specific columns):
```bash theme={null}
synqcli advisor \
--entity-id "postgres::public::users" \
--columns "status,email,role" \
--instructions "Suggest tests for these columns"
```
This will create separate YAML files for each entity in the output directory.
When --deploy is specified, all YAML files in the output directory will be deployed.
DWH CONNECTIONS:
When --connections is specified, the advisor can profile columns to:
* Discover actual values for accepted\_values tests
* Determine min/max bounds for range tests
* Check null rates and distinct counts
The connections file uses the DWH agent's format and supports every warehouse
the agent does (reference: [https://schemas.synq.io/synq-dwh/v1/config.html](https://schemas.synq.io/synq-dwh/v1/config.html)):
```bash theme={null}
connections:
my-postgres:
postgres:
host: localhost
port: 5432
database: mydb
username: user
password: ${PG_PASSWORD}
```
The older list format (- id: ..., type: ...) and the DWH\_TYPE, DWH\_HOST,
DWH\_PORT, DWH\_DATABASE, DWH\_USERNAME and DWH\_PASSWORD variables still work for
postgres, mysql, bigquery, snowflake, clickhouse, redshift and databricks.
| Flag | Type | Default | Description |
| - | - | - | - |
| `--auto-confirm` | `bool` | — | Automatically confirm deployment prompts (only used with --deploy) |
| `-C`, `--columns` | `stringSlice` | — | Filter test suggestions to specific columns (can be comma-separated or specified multiple times) |
| `-c`, `--connections` | `string` | — | Path to DWH connections YAML file for data profiling |
| `--deploy` | `bool` | — | Deploy generated YAML files after creation (requires --output) |
| `-e`, `--entity-id` | `stringSlice` | — | Entity ID(s) (table FQN) to suggest tests for (can be specified multiple times, required) |
| `-f`, `--force` | `bool` | — | Force overwrite existing YAML files (default: skip if exists) |
| `-i`, `--instructions` | `string` | — | Instructions for what tests/monitors to suggest (required if --instructions-file not provided) |
| `-I`, `--instructions-file` | `string` | — | Path to file containing instructions (required if --instructions not provided) |
| `-n`, `--namespace` | `string` | — | Namespace for generated YAML (default: synq-advisor) |
| `-o`, `--output` | `string` | — | Output directory path for YAML files (required if --deploy is used) |
| `-p`, `--print-protobuf` | `bool` | — | Print protobuf messages in JSON format during deployment |
| `-s`, `--severity` | `string` | — | Default severity for tests/monitors (INFO, WARNING, ERROR) |
| `-v`, `--verbose` | `bool` | — | Show verbose output including loaded schema and LLM interactions |
### `synqcli auth`
Manage Coalesce Quality API authentication
```bash theme={null}
synqcli auth [flags]
```
Manage authentication for the Coalesce Quality API.
synqcli can authenticate as a human in a browser, not only with client
credentials. Credentials are cached under \~/.synq/oauth/, partitioned by region,
and shared with the other Coalesce Quality CLIs by default.
Client credentials (--client-id / --client-secret or QUALITY\_CLIENT\_ID /
QUALITY\_CLIENT\_SECRET) and a pre-issued QUALITY\_TOKEN still take precedence over
a browser login, so CI is unaffected.
Pick a deployment with --region, or point at a staging or self-hosted deployment
with --endpoint. Both work on every command, and a successful login is
remembered — so after logging in to one deployment, later commands need neither
flag.
Examples:
```bash theme={null}
synqcli auth login
synqcli auth login --region us
synqcli auth status
synqcli auth use au
synqcli auth logout --region us
```
| Flag | Type | Default | Description |
| - | - | - | - |
| `--auth-profile` | `string` | — | Credential profile to read or write (default: the shared profile) |
#### `synqcli auth login`
Authenticate with Coalesce Quality in a browser
```bash theme={null}
synqcli auth login [flags]
```
Authenticate using the OAuth2 authorization code flow with PKCE.
Opens a browser, then caches the resulting credential under \~/.synq/oauth/. The
login asks for only the permissions synqcli needs — managing monitors, SQL tests
and query-based deployment rules, plus the reads required to resolve the assets a
check targets.
By default the credential goes to the shared slot, so one login serves every
Coalesce Quality CLI on this machine. Pass --isolated to keep it to synqcli.
| Flag | Type | Default | Description |
| - | - | - | - |
| `--isolated` | `bool` | — | Store a credential for synqcli alone instead of the shared one |
#### `synqcli auth logout`
Remove the stored credential for a region
```bash theme={null}
synqcli auth logout
```
Delete the cached credential for one region, leaving credentials for other
regions in place. The OAuth client registration is kept, so logging back in does
not create another connected app.
#### `synqcli auth status`
Show every stored credential, for every region
```bash theme={null}
synqcli auth status
```
List the cached credentials across all regions, with the workspace, granted
permissions and expiry of each. Reads local files only — no network calls.
Pass --region or --endpoint to show one deployment instead of all of them.
#### `synqcli auth token`
Print the access token, refreshing it if needed
```bash theme={null}
synqcli auth token
```
Print the current access token to stdout, refreshing it first if it has expired
or is close to expiring.
Useful for scripting:
```bash theme={null}
curl -H "Authorization: Bearer $(synqcli auth token)" ...
```
#### `synqcli auth use`
Show or set the deployment commands use by default
```bash theme={null}
synqcli auth use [region|endpoint] [flags]
```
Record which deployment a command talks to when it is given neither --region
nor --endpoint.
A successful login already records the deployment it authenticated against, so
this is only needed to switch between two deployments you are logged in to. An
explicit --region or --endpoint, QUALITY\_API\_ENDPOINT and QUALITY\_REGION all
still win over what is recorded here.
Called with no argument it prints the current setting.
Examples:
```bash theme={null}
synqcli auth use
synqcli auth use au
synqcli auth use developer.synq.dev:443
synqcli auth use --clear
```
| Flag | Type | Default | Description |
| - | - | - | - |
| `--clear` | `bool` | — | Forget the recorded deployment and fall back to the default region |
### `synqcli deploy`
Deploy custom monitors from YAML configuration
```bash theme={null}
synqcli deploy [FILES...] [flags]
```
Deploy custom monitors by parsing YAML configuration files.
Before deploying, it prints what changes will be made and prompts for confirmation,
unless --auto-confirm is set.
If no files are provided, it will recursively search for YAML files from the working directory.
| Flag | Type | Default | Description |
| - | - | - | - |
| `--auto-confirm` | `bool` | — | Automatically confirm all prompts (skip interactive confirmations) |
| `--dry-run` | `bool` | — | Show what would be deployed without actually deploying |
| `--log-file` | `string` | — | Path to write execution log in JSONL format (or \$QUALITY\_LOG\_FILE) |
| `--namespace` | `stringSlice` | — | If set, will only make changes to the included namespaces |
| `-p`, `--print-protobuf` | `bool` | — | Print protobuf messages in JSON format |
| `--verbose` | `bool` | — | List every affected asset in the deployment-rule preview instead of capping the list |
### `synqcli export`
Export custom monitors, SQL tests, and deployment rules to YAML
```bash theme={null}
synqcli export [output-file] [flags]
```
Export custom monitors, SQL tests, and deployment rules as YAML.
By default every resource type is exported. Use --type to narrow:
```bash theme={null}
synqcli export out.yaml # everything
synqcli export out.yaml --type=sql-tests # only SQL tests
synqcli export out.yaml --sql-test= # auto-narrows to that one test
synqcli export out.yaml --type=monitors --sql-test= # all monitors + that test
```
ID-scoped flags (--monitor, --sql-test, --deployment-rule,
\--sql-test-deployment-rule) implicitly add their owning type to the selection,
so you rarely need --type explicitly.
| Flag | Type | Default | Description |
| - | - | - | - |
| `--deployment-rule` | `stringArray` | — | Limit exported monitor deployment rules by rule IDs. AND'ed with other scopes. |
| `--integration` | `stringArray` | — | Limit exported monitors and SQL tests by integration IDs. Does not narrow deployment rules. AND'ed with other scopes. |
| `--monitor` | `stringArray` | — | Limit exported monitors by monitor IDs. AND'ed with other scopes. |
| `--monitored` | `stringArray` | — | Limit exported monitors, SQL tests and static deployment rules by monitored asset paths. Does not narrow query-based rules. AND'ed with other scopes. |
| `--namespace` | `string` | — | Namespace for generated YAML config |
| `--source` | `string` | `app` | Limit exported monitors by source. One of \[app api all]. Defaults to "app". AND'ed with other scopes. |
| `--sql-test` | `stringArray` | — | Limit exported SQL tests by test IDs. AND'ed with other scopes. |
| `--sql-test-deployment-rule` | `stringArray` | — | Limit exported SQL test deployment rules by rule IDs. AND'ed with other scopes. |
| `--type` | `stringArray` | — | Limit exported resource types. Repeatable; accepted values: \[monitors sql-tests deployment-rules sql-test-deployment-rules]. Unset = all. ID-scoped flags auto-include their type. |
### `synqcli schema`
```bash theme={null}
synqcli schema
```
Generate JSON schema.
The schema can be used in IDEs for autocomplete and validation.
### `synqcli upgrade`
Upgrade synqcli to the latest release
```bash theme={null}
synqcli upgrade [flags]
```
Download the latest synqcli release and replace this binary with it.
The archive for this platform is verified against the release's checksums.txt and
the new binary is run once, before anything is replaced, to prove it works on
this machine. If the directory holding the binary is not writable — /usr/local/bin
usually is not — nothing is downloaded and the reason is printed.
\--check reports what an upgrade would do and changes nothing.
Coalesce Quality CLIs also mention a newer release once a day, on stderr, when
one exists. That check resolves a tag from a public GitHub URL, never blocks the
command it runs beside, and never reports its own failures — so an air-gapped or
offline machine behaves exactly like an up-to-date one. Set QUALITY\_NO\_UPDATE\_CHECK=1
(or DO\_NOT\_TRACK=1) to switch it off entirely; it is already silent in CI, when
output is not a terminal, and inside a container.
Examples:
```bash theme={null}
synqcli upgrade --check
synqcli upgrade
```
| Flag | Type | Default | Description |
| - | - | - | - |
| `--check` | `bool` | — | Report what an upgrade would do, and change nothing |
| `--force` | `bool` | — | Install the latest release even if this binary is already it, or if the archive disagrees with the release tag |
# Setting up deployment rules
Source: https://docs.synq.io/monitors/deployment-rules
Deployment Rules define where and how monitors apply across your data stack. Instead of manually adding individual monitors, you can set up rules that dynamically apply the right monitors based on your data source, Data Product, or specific table groups.
**Key Benefits**:
* Faster setup – Apply monitoring in bulk instead of configuring each asset manually
* Better control – Adjust sensitivity and thresholds across multiple monitors at once
* More flexibility – Define and manage exclusions and overrides
## Setting Up Deployment Rules
* Navigate to `Deployment Rules` under `Health`
* Click **Create Deployment Rule**
* Choose the `Sensitivity` ([read more](/monitors/monitor-sensitivity)), `Severity`, and `Metrics` ([read more](/monitors/monitors-overview))
* Select the assets you want to monitor
Avoid applying rules too broadly—too many alerts can lead to noise. Use exclusions and overrides to refine rule coverage.
* You can also set up Exclusion rules to exclude specific assets from a rule under the `Deployment Rules` page.
## Adjusting & Managing Deployment Rules
Once configured, Deployment Rules will automatically apply monitoring. However, you can still refine them. On the `Deployment rules` page:
* **Edit a rule** to add/remove assets
* **Change sensitivity settings** if alerts are too frequent or too infrequent
* **Disable a rule** if it no longer applies
## What a rule deploys
A deployment rule pairs a selection with one of two payloads:
* **`table_stats`** — the monitors and metrics covered above, deployed onto every asset the selection matches.
* **`sql_tests`** — the [SQL tests](/monitors/sql-tests) listed under the rule's own `tests:`, deployed onto every table or view the selection matches.
Both kinds live in the same `deployment_rules:` list of a [monitors-as-code](/monitors/monitors-as-code) configuration:
```yaml theme={null}
version: v1beta2
deployment_rules:
- name: PII email checks
type: sql_tests
resolver_ql: with_columns("email")
schedule: daily
severity: ERROR
keep_removed_tests: false
tests:
- type: not_null
columns: [email]
- type: business_rule
name: email looks like an address
sql_expression: email NOT LIKE '%@%'
```
Every field, with its type and validation rules, is in the [configuration reference](https://schemas.synq.io/synq-monitors/v1/config.html); the commands that deploy a configuration are in the [CLI reference](/monitors/cli). The rest of this section is what a `sql_tests` rule does once it is deployed.
### Only the tables that carry the columns
A test is deployed onto a matched table only if that table has every column the test reads — the columns it checks, any `select_columns`, a `unique` or `relationships` test's `time_partition_column`, and for `relationships` the columns on the referenced table as well. A table missing one of them shows up in the deploy plan as a skipped test and gets no test at all: such a test could only fail every run.
That makes a broad selection safe to write. A rule carrying three tests covers each matched table with the ones that fit it.
A test is skipped whole, never narrowed. A `not_null` on `[email, account_id]` against a table that has only `email` is not reduced to `email` alone, because `unique(a, b)` is a different assertion from `unique(a)`. Declare one test per column when you want that granularity.
A `business_rule` is gated on its `select_columns` alone: its SQL expression is never parsed, so a rule whose expression reads `status` without declaring it deploys anyway, while one that declares `select_columns: [status]` is skipped on every table without that column. A `business_query` declares no columns of its own and is never gated.
A matched table whose schema has not been ingested keeps the tests it already carries and is retried on the next sync; a test not yet deployed there waits rather than being created, since whether the columns are present cannot be answered either way.
### When a table leaves the selection
The tests a rule deployed onto a table are deleted once that table stops matching. Set `keep_removed_tests: true` to leave them in place instead. The rule then stops resyncing those tests altogether, and they are yours to remove if they start failing.
A column dropped from a table the rule still matches is a different case: that table's test goes away on the next sync, and `keep_removed_tests` does not protect it. The flag is about the table leaving the selection, not about a test losing what it reads.
### Deleting a rule deletes the tests it deployed
Editing a rule's selection resyncs it. Tests appear on the tables the edited selection matches, go away from the tables it stops matching, and tables matched both before and after keep the tests they already had.
Deleting a `sql_tests` rule deletes every test the rule deployed, together with those tests' check entities and their history. Nothing brings them back.
A rule is addressed by its `id:` when it carries one, and by its name within its namespace when it does not — so renaming a rule that has no `id:` replaces it: the old rule is deleted with everything it deployed, and the next scheduled sync builds the tests again from scratch under the new name. Give a rule an `id:` before you need to rename it, and renaming is an update instead.
### A relationships test names one reference table
A `relationships` test under a rule names one reference table per `references[]` entry, and every matched table is checked against that same table — many tables carrying `customer_id` against one `dim.customers`. The reference is not resolved per match, so a rule cannot express "each `staging.X` against its own `raw.X`". Write those tests one per table, under the entity they belong to.
The reference table has to sit in the same integration as the matched tables, because the test runs on the matched table's connection. Nothing narrows the selection to that integration for you, so confine it yourself with `with_integration_ids(...)`. A match in another integration gets a test whose reference resolves on the wrong connection: it fails every run, or it joins a table that happens to share the name.
A referenced table that has not been ingested leaves the test waiting indefinitely, and the deploy plan says so, naming the table.
## Best Practices for Deployment Rules
See [Monitors overview](/monitors/monitors-overview) for best practices when setting up monitors. Specifically for deployment rules, we recommend you consider:
* **Use exclusions to reduce noise** - remove temporary tables, or datasets where fluctuations are expected.
* **Override sensitivity** - for example, for financial data configure tighter alert thresholds to detect even small anomalies. For less important data set a looser threshold to only get alerted on significant deviations.
## Managing deployment rules programmatically
A rule's selection, metrics, sensitivity and exclusions can be read, previewed and
deployed outside the app.
* **Public API** — `synq.monitors.automated_monitors.v1.DeploymentRulesService`.
Every method on it, reads included, needs a token with
`SCOPE_MONITORS_AUTOMATIC_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [deployment rule tools](/scout/mcp#deployment-rules) let an AI
assistant author a selection, see exactly which monitors a rule would create,
keep and delete before anything happens, and deploy it once you approve.
Previewing is side-effect-free and doubles as validation of the selection
query; deploying and deleting are confirmation-gated and need the Deploy
permission.
# Monitor feedback
Source: https://docs.synq.io/monitors/monitor-feedback
Monitor feedback allows you to provide input on detected anomalies so our model can learn from your business context and reduce false positives over time.
When you update the status of an issue, you can choose from five available statuses. Only **Expected** changes the behavior of the model; the other statuses are primarily informational for cross-team communication.
## Status Types
### **Investigating** (Informational)
Use when you or your team are actively looking into the issue.
* **Effect:** No change to the model.
* **Example:** You want to communicate that the issue is being worked on to avoid duplicate investigations by other team members.
### **Expected** (Model-Affecting)
Use when the new value was expected and should no longer be flagged as an anomaly.
* **Effect:** The anomaly will be added to the model's training dataset during the next update, and thresholds will adjust to prevent similar false positives in the future.
* **Example:** You increased a metric intentionally (e.g., a new product launch spiked signups).
### **Fixed** (Informational)
Use when you have taken action to resolve the underlying issue and expect the monitor to succeed on the next run.
* **Effect:** No change to the model. By default, the issue will remain flagged until it passes at the infrastructure level (monitor falls back within threshold, transformation layer test passes, etc.).
* **Example:** Missing events in a table were restored and you expect the next run to succeed.
> **Workspace Settings:**\
> If **Fixed** is enabled under *Workspace Settings → Resolved Issue Statuses*, the issue will be immediately removed from triage and moved to a non-error state — even if it is not yet resolved at the infrastructure level.\
> **Recommended:** Keep this **unchecked** so the issue stays in error state until technically resolved.
### **No Action Needed** (Informational)
Use when no action is required and the issue will resolve itself on the next run.
* **Effect:** No change to the model.
* **Example:** A temporary data delay that you know will catch up in the next scheduled run.
### **Reset**
Use to revert a previously set status (especially *Expected*) so that the issue returns to triage and can be investigated again.
* **Effect:** Any model adjustments made due to marking as *Expected* will be undone, ensuring the monitor continues to flag this type of anomaly.
* **Example:** You initially marked an anomaly as expected, but later discovered it was a genuine issue and want the monitor to detect it again.
## Marking Statuses
You can set or change a status directly from the monitor view:
* **Investigating** – Marks the issue as being worked on.
* **Mark as Expected** – Flags the anomaly as expected and updates thresholds.
* **Mark as Fixed** – Indicates the issue has been resolved manually.
* **Mark as No Action Needed** – Indicates the issue requires no action and will self-resolve.
* **Reset Status** – Clears any previously set status so the issue is treated as new.
## Workspace Settings: Resolved Issue Statuses
Under **Workspace Settings → Resolved Issue Statuses**, you can configure which statuses automatically remove issues from triage.
* **If checked:** The issue is immediately cleared from triage and marked non-error, regardless of whether the underlying infrastructure is still failing.
* **If unchecked (recommended for Fixed):** The issue will remain in error state until technically resolved (e.g., monitor passes, transformation layer test succeeds).
By combining **Expected** for model training with careful use of **Fixed**, **No Action Needed**, and **Investigating** for team communication, you can keep noise low while ensuring genuine issues are surfaced.
# Configuring monitor sensitivity
Source: https://docs.synq.io/monitors/monitor-sensitivity
You can adjust each monitor's sensitivity to best fit your risk tolerance for the underlying data. We recommend you only adjust monitors up front if you have preconditioned expectations for your data (e.g., a monitor on a financial metric may need to detect even small changes) or adjust if a monitor triggers too often/not often enough. Monitors are set to *balanced* by default.
* **Precise**—this setting raises an anomaly even on subtle changes. It should be used when you want to detect issues with high precision (e.g., on a financial metric) and is the best fit for stable time series. Precise monitors will alert you more quickly, and won’t be able to check if the monitors return to normal in the next measurement.
* **Balanced (default)**—the default setting that raises anomalies based on a balanced threshold. The model will wait to see if the monitor returns to normal on the next measurement before alerting. Recommended for most monitors.
* **Relaxed**—this setting raises an anomaly only on significant changes. It should be used when you’re less sensitive to small changes but still want to know about significant changes. Relaxed monitors will check if the monitor returns to normal in the next two measurements before alerting you.
**Best practices for configuring sensitivity:**
Unless you have clear expectations for your data, we recommend you start with a *balanced* monitor and see how that performs on the first predictions. If the monitor is not precise enough to catch anomalies, adjust the sensitivity to *precise*. On the other hand, if you find yourself receiving too many alerts, consider changing the sensitivity to *relaxed* or rethink if you need the monitor in the first place.
## Configuring the sensitivity of one monitor
Select a monitor, and under `Settings`, select a sensitivity level. The monitor will be updated immediately to reflect the selected sensitivity level.
## Configuring the sensitivity of multiple monitors
You can adjust the sensitivity of multiple monitors in one go if you have multiple tables with similar expectations for what constitutes an anomaly.
In the [health section](https://app.synq.io/healthz/monitors), select all the monitors you want to bulk edit, click `Select action`, and `Change sensitivity` to the updated sensitivity level. All selected monitors will have their sensitivity level updated immediately.
In cases where you have many monitors, you can use the filter menu at the top to narrow down your selection. If you want to search for monitors that match a specific string, use the `Query` option.
# Defining monitors in code
Source: https://docs.synq.io/monitors/monitors-as-code
Instead of defining your monitor placement specifications in the UI, you can also provide a YML file to specify where to place monitors programmatically. This is beneficial if you prefer to manage monitor placement through code, e.g., enforce it through your CI/CD development process.
## Getting started
1. Install `synqcli` from the [releases page](https://github.com/getsynq/synqcli/releases), or follow the [installation instructions](https://github.com/getsynq/synqcli#installation) for your platform
2. Authenticate. For interactive use, `synqcli auth login` opens a browser; for CI, set up an [API Client](https://app.synq.io/settings/api) by clicking `Add client` with the following scopes
* `Edit SQL Tests`
* `Edit Automatic Monitors`
* `Edit Custom Monitors`
and pass them as `QUALITY_CLIENT_ID` / `QUALITY_CLIENT_SECRET`
3. Run `synqcli deploy` against your YAML
Every command and flag is in the [CLI reference](/monitors/cli).
YAML is one of three ways to manage monitors outside the app. The
[public API](/api-reference/api) drives the same services directly, and an AI
assistant can create and change monitors over
[MCP](/scout/mcp#managing-monitors-and-tests). A monitor deployed from YAML stays
managed by its config: the app will not edit it, and an MCP write that would
overwrite it takes an explicit extra confirmation.
## Defining monitors in code
Create a YAML file where you'll be managing the monitor setup, for example, `synq_monitors.yml`. You can split your configuration up into multiple files and use namespaces to manage individual domains or data products independently.
**Using Namespaces**
Monitors in one namespace are isolated from those in another, which helps you:
* Avoid conflicts when multiple teams manage monitors in parallel.
* Keep different pipelines or environments separate (e.g., transformation models in CI vs. external tables in prod).
* Apply ownership, defaults, and alerts consistently within a group.
You can define a `namespace` at the top of your YAML file.
Example content of the file
`version: v1beta2` is the current format. A file with no `version:` key is read as the
legacy flat `monitors:` layout, which exists for compatibility only — do not author new
files in it.
```yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-monitors/v1/config.schema.json
version: v1beta2
namespace: "data-team-pipeline"
defaults:
severity: ERROR
entities:
- id: bq-synq-demo::nyc_taxi::orders
time_partitioning_column: created_at
monitors:
- id: freshness_on_orders
type: freshness
expression: "created_at"
- id: bq-synq-demo::nyc_taxi::request_logs
time_partitioning_column: at
monitors:
- id: volume_on_logs
type: volume
segmentation:
expression: "country"
filter: "country IN ('US', 'CA')"
- id: bq-synq-demo::nyc_taxi::users
time_partitioning_column: registered_at
monitors:
- id: stats_on_user_fields
type: field_stats
columns:
- age
- column: signup_method
metrics:
- NUM_UNIQUE
- { metric: PCT_NULL, sensitivity: PRECISE }
mode:
anomaly_engine:
sensitivity: BALANCED
schedule:
type: daily
query_delay: 2h
- id: bq-synq-demo::nyc_taxi::active_users
time_partitioning_column: registered_at
monitors:
- id: custom_numeric_active_users
type: custom_numeric
metric_aggregation: "COUNT(DISTINCT user_id)"
mode:
fixed_thresholds:
min: 100
max: 10000
schedule:
type: hourly
query_delay: 15m
```
## Configuring a monitor
You can define custom monitor types as code (`freshness`, `volume`, `custom_numeric`, and `field_stats`). [Read more about each monitor type](https://docs.synq.io/monitors/monitors-overview).
### Field stats columns and metrics
For `field_stats`, each entry under `columns:` is either a plain column name or a `column` with a `metrics` list. The column must be a string, numeric, integer, float, date or datetime column; any other type rejects the deploy.
* A plain column name monitors every metric available for the column's data type.
* `column` with `metrics` restricts what is predicted to the listed metrics.
* A metric is an id string or `{ metric, sensitivity }`. A metric's `sensitivity` (`PRECISE`, `BALANCED` or `RELAXED`) overrides the monitor's `mode.anomaly_engine.sensitivity`.
* A metric must exist for the column's data type, otherwise the deploy is rejected. A column or a metric listed twice is an error. See [metrics by data type](/monitor-types/fieldstats#metrics-tracked-automatically).
* Changing the selection updates the monitor in place: it does not reset the monitor or change its id. A removed metric stops being predicted, and adding it back resumes its history.
### Obtaining IDs from the UI
Each entry under `entities:` is one asset, and its `id:` is the full table identifier as represented in the platform. You can locate this ID by navigating to a table in the UI (using the catalog or search functionality) and copying the ID from the URL.
In the example below, `bq-synq-demo::nyc_taxi::financial_statement` will be the ID.
### Understanding the configurable parameters
Every configurable parameter, with its type and validation rules, is in the [configuration reference](https://schemas.synq.io/synq-monitors/v1/config.html). The same schema drives editor autocompletion — put the `# yaml-language-server:` line from the example above at the top of your own file.
For more examples of configuring individual monitors, see the [examples directory](https://github.com/getsynq/synqcli/tree/main/examples/v1beta2).
## Covering assets by query instead of listing them
A monitor or test under an `entities[].id` targets one asset. To cover many assets by a
rule instead of naming each one, write a top-level `deployment_rules:` list: each entry
carries a selection, and every asset matching it is covered, including assets that start
matching later. A rule's `type:` says what it deploys — `table_stats` for monitors,
`sql_tests` for the tests listed under the rule itself. See
[deployment rules](/monitors/deployment-rules#what-a-rule-deploys).
`deployment_rules:` is read only under `version: v1beta2`. A file with no `version:` key
is read as the flat layout, which has no such key — the list is accepted, no rule
is created, and `synqcli deploy` reports success. Declare the version at the top of the
file before adding a rule to it.
## Verifying monitors in the UI
If you navigate to the `Settings` menu for a monitor, you can verify that it's created by code by seeing the `Monitor was created via API and can't be managed in APP` label.
# Monitors overview
Source: https://docs.synq.io/monitors/monitors-overview
Monitors are a core concept in Coalesce Quality, helping you proactively detect issues through self-learning and powerful anomaly detection
Monitors help you proactively detect issues such as missing or stale data, a change in a segment within your data, or a drop in a metric. The platform supports multiple monitor types, but it’s helpful to distinguish them by
* **Automatic monitors**—queries your metadata and can be set up with little configuration.
* **Custom monitors**—queries your raw data and requires a more custom implementation to support advanced use cases.
External tests (e.g. from dbt, SQLMesh, or Coalesce Transform) don't count toward your monitor limit. Integrate them to get a complete 360° view of your data health.
### Automatic monitors
Automatic monitor queries your metadata such as the [Snowflake Information Schema](https://docs.snowflake.com/en/sql-reference/info-schema). This means they’re easy to configure as you don’t have to define a custom time dimension. As automatic monitors only query metadata, they incur minimal additional processing costs.
The table stats monitor is an all-in-one monitor for row count, freshness, and delay. It's out recommended automatic monitor.
The freshness monitor looks at a table's timestamp and alerts you if it’s been too long since the last update.
The volume monitor looks at the number of rows in a table to detect how much data is added or removed and alerts you of abnormal increases or decreases.
The schema monitor tracks added, removed, and retyped columns on every table and view automatically. Set up a schema change alert to be notified.
### Custom monitors
Custom monitors can help detect issues more nuanced to your business, such as a custom monitor detecting a change in business metrics or a drop in one segment. Custom monitors query your raw data and should be used selectively to avoid incurring unnecessary costs. Custom monitors have to be configured individually by, e.g., specifying a time dimension, a field to group the monitor by, and custom SQL for an aggregate metric (e.g., `avg(revenue)`)
The custom volume monitor detects issues in the volume of data in a time series and is highly configurable.
The custom monitor detects issues in a time series, as represented by your specific SQL statement.
The field stats monitor detects issues in the health of a specific field (e.g., %, not null or % empty values).
### How monitors work
Most monitors are based on a time series model to predict the expected value of, e.g., the number of rows or a business metric you specified as an SQL statement. If a new data point falls significantly outside our prediction confidence interval, we alert you of an anomaly. Our model uses seasonal decomposition to build the most robust anomaly detection.
* **Seasonality**—our models account for intraday seasonality. This helps us learn if patterns, such as a spike by the start of the business day, are expected behavior.
* **Trend**—many datasets have a trend - e.g., a transactions table that’s always increasing as the business acquires more users. We detrend the data to ensure we only alert you of abnormal behaviors.
* **Sensitivity**—the algorithm looks at the past few days to understand the volatility of the data. If data changes a lot, it accepts a broader range of values. If the values are stable, they tighten the range and become more sensitive.
* **Trigger thresholds**—we detect patterns of anomalies. In case of a sharp change, we alert immediately. If we detect smaller anomalies, we wait for confirmation by the next observation to avoid triggering temporary small deviations.
### Monitor severity
Each monitor has a severity that helps you understand the current state.
* **OK**—the monitor is active, and no issues have been detected
* **Error**—the monitor is active, and an unresolved issue has been detected
* **Fatal**—the monitor is not functioning as expected (e.g., due to insufficient permissions to the data warehouse).
* **Inactive**—the monitor hasn’t yet finished learning the historical patterns of your data, or there’s not sufficient data for us to train the monitor
*Selecting a monitor shows you the current severity*
*See all monitors by type and their severity in the [Health overview](https://app.synq.io/healthz/overview)*
# Muting monitors
Source: https://docs.synq.io/monitors/muting-monitors
If you get alerted about an issue you cannot address immediately, you can snooze the alert. Snooze will suppress new alerts for the next 7 days, which gives you time to fix the underlying issue.
Both Coalesce Quality monitors and transformation layer tests (from dbt, SQLMesh, or Coalesce Transform) can be snoozed.
## Muting or unmuting a monitor
Open a monitor, click `Settings`, and toggle `Snooze all alerts` to mute the monitor for 7 days.
## Muting or unmuting multiple monitors
You can mute multiple monitors in one go.
In the [health section](https://app.synq.io/healthz/monitors), select all the monitors that you want to bulk edit, click `Select action`, and `Mute (7 Days)` to snooze the selected monitors for 7 days.
In cases where you have many monitors, you can use the filter menu at the top to narrow down your selection. If you want to search for monitors that match a specific string, use the `Query` option
# Setting up monitors
Source: https://docs.synq.io/monitors/setting-up-monitors
Anomaly monitors can help you catch important issues you may otherwise not have caught and are a core component of building reliability in business-critical data products.
## Monitor setup considerations
It’s essential to balance coverage and signal-to-noise ratio. While maximal monitoring across the stack may be appealing, it is not always the best option. It can cause alert fatigue and wear out your data team, causing important issues to be overlooked. The right monitor placement strategy can help with this.
While there’s no one-fit-all monitoring strategy, here are our recommendations for how to approach setting up monitors. Reach out to [support@coalesce.io](mailto:support@coalesce.io) to schedule a chat if you want advice on setting up monitors for your specific situation.
We've written extensively about how to best set up your monitoring in the [Data Product's guide](https://coalesce.io/reports/the-definitive-guide-to-building-data-products/). Here are a few guidelines.
#### Designing a Testing Strategy
A well-structured testing strategy ensures comprehensive coverage across the data platform while minimizing redundancy. This involves defining testing approaches for each layer—sources, transformations, and data products—and considering how transformation layer tests (from dbt, SQLMesh, or Coalesce Transform) complement anomaly monitors to avoid redundant testing.
#### Testing Sources
Testing at the source layer is critical because errors propagate downstream. Since this layer has the most dependencies, a strong foundation reduces the need for excessive testing further down the pipeline. See more in the [Testing sources](https://coalesce.io/reports/the-definitive-guide-to-building-data-products/) section of our guide.
#### Testing Transformations
Once sources are well-tested, transformation testing can focus on verifying changes rather than duplicating checks already performed upstream. This includes validating logic and aggregations after joins. A targeted approach ensures transformations produce accurate and reliable results without unnecessary overhead. See more in the [Testing transformations](https://coalesce.io/reports/the-definitive-guide-to-building-data-products/) section in our guide
#### Testing Data Products
Data product testing should be targeted and ideally done by data people with the relevant domain expertise. Instead of focusing on technical aspects of data, when testing data products, you should verify the logic encoded in SQL transformations. See more in the [Testing products](https://coalesce.io/reports/the-definitive-guide-to-building-data-products/) section of our guide.
## Setting up monitors
There are two ways to set up monitors in the app.
### 1. Add a monitor to an individual table
Set up monitors one table at a time. This is great for targeted monitoring where you just want to monitor specific tables. It's also the only way to define custom monitors.
1. Navigate to the table or data model (e.g., by using the Catalog or Search functionality)
2. Click the `+Add` button and select a monitor type
### 2. Deploy monitors at scale
Use deployment rules to deploy monitors to many tables at once. For example, you can automatically add monitors to all sources upstream of a data product - the rule will also ensure that new sources added automatically will have monitors deployed.
See [Setting up deployment rules](/monitors/deployment-rules) for how to get started
## Setting up monitors programmatically
Monitors can also be created and changed outside the app, and all routes drive the
same services — a monitor deployed from CI or created by an AI assistant is the
monitor the app shows, with the same settings and the same history.
* **YAML** — keep monitor placement in version control and deploy it from your
CI pipeline with `synqcli`. See
[Defining monitors in code](/monitors/monitors-as-code).
* **Public API** — `synq.monitors.custom_monitors.v1.CustomMonitorsService`
covers custom monitors and
`synq.monitors.automated_monitors.v1.DeploymentRulesService` the rules that
deploy monitors at scale. Reading monitors needs a token with
`SCOPE_MONITORS_READ`; creating and editing them needs
`SCOPE_MONITORS_CUSTOM_EDIT`, and anything touching deployment rules needs
`SCOPE_MONITORS_AUTOMATIC_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the
[monitor and test tools](/scout/mcp#managing-monitors-and-tests) let an AI
assistant pick a check type for a goal, create the monitor, and adjust it
later. Listing monitors is covered by read-only consent; creating, updating
and deleting need the Deploy permission, and a change that would reset a
monitor's baseline is confirmation-gated.
# SQL Tests
Source: https://docs.synq.io/monitors/sql-tests
SQL tests let you write bespoke tests that fit your business circumstances and can be run on any table tracked in the platform through a unified workflow.
### Test types
Alongside a query you write yourself, a test can be one of the following types. Each takes the columns and values it needs, so a test of a given type is the same assertion wherever it is defined — in the app, over the API, or in a YAML configuration, where the name in the first column is the `type:` value.
| Type | What it checks |
| - | - |
| `not_null` | Column values are never null |
| `empty` | Column values are not empty — a null or whitespace-only value counts as empty |
| `unique` | Column values are unique, optionally within a time window |
| `accepted_values` | Values are in an allowed list |
| `rejected_values` | Values are not in a forbidden list |
| `min_max` | Values fall within a numeric or date range |
| `min_value` | Values meet or exceed a minimum |
| `max_value` | Values stay at or below a maximum |
| `freshness` | Data has been updated within a time window |
| `relative_time` | One timestamp column never comes after another, for example `order_date` at or before `ship_date` |
| `business_rule` | A SQL condition identifying the rows that violate the rule |
| `business_query` | A full `SELECT` whose result rows are reported as failures, or a rollup graded by [evaluators](#evaluators) |
| `relationships` | Referential integrity — every source value exists in a reference table |
### Creating a SQL test
* Head to the [Health overview](https://app.synq.io/healthz/overview)
* In the SQL tests section, click [`Add sql test`](https://app.synq.io/healthz/sql-tests/create)
* Select the connection to execute your SQL query
* Specify a SQL query. The test is considered a success if it returns zero records. If any records are returned, the test will trigger an error, and the failed records will be stored in an [audit table](#audit-table-schema) for investigation. To have the query return metrics and grade them individually instead, add [evaluators](#evaluators).
* Choose a schedule (`hourly/daily`) and a time (`hh: mm`) when you want the test to run.
Running an excessive amount of tests or running a test too often will impact your data warehouse costs. Avoid running tests more often than needed
* The confirmation page will show you a summary of the setup. To make it easier to locate in the UI, you can give the test a human-friendly name.
### Editing a SQL test
* Head to the [Health overview](https://app.synq.io/healthz/overview)
* Click on [SQL tests](https://app.synq.io/healthz/monitors?filters=%5B%5B%7B%22kind%22%3A%22Type%22%2C%22types%22%3A%5B1421%5D%7D%5D%5D) to see all your SQL tests
* Select the SQL test you want to edit by clicking on it
* In the popout, navigate to the `settings` tab. Click `Edit` under SQL test configurations to edit the name, run schedule, or SQL code. To delete the test, click `Delete SQL test`
### Managing SQL tests programmatically
SQL tests can be created and changed outside the app, one at a time or in bulk
across every table matching a selection.
* **YAML and the CLI** — tests live under an entity's `tests:` in a
[monitors-as-code](/monitors/monitors-as-code) configuration and are deployed with
`synqcli deploy`. To cover many tables by a selection instead of listing each one,
put the tests under a `type: sql_tests`
[deployment rule](/monitors/deployment-rules#what-a-rule-deploys).
* **Public API** — `synq.datachecks.sqltests.v1.SqlTestsService` for individual
tests and `synq.datachecks.sqltests.v1.SqlTestsDeploymentRulesService` for
rules that deploy a set of tests onto every table a selection matches.
Reading needs a token with `SCOPE_DATACHECKS_SQLTESTS_READ`, writing needs
`SCOPE_DATACHECKS_SQLTESTS_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [SQL test tools](/scout/mcp#managing-monitors-and-tests) create
tests from a template (not-null, unique, accepted values, relationships,
freshness, range, business rules) or from raw SQL, and the
[deployment rule tools](/scout/mcp#sql-test-deployment-rules) do the same
across a selection of tables. Listing tests is covered by read-only consent;
writing needs the Deploy permission, and every write is confirmation-gated.
### The table placeholder
A `business_rule` and a `business_query` carry SQL you write yourself, so nothing in them names the table unless you do. Write `{{ table }}` and it is replaced with the fully qualified name of the table the test is anchored to, quoted for your warehouse. It is the only supported placeholder — any other `{{ name }}` token is rejected when the test is saved.
The substitution is textual, so the token is replaced everywhere it appears, inside a string constant or a comment as well as in a `FROM` clause. There is no escape sequence; write the name out yourself if you need the literal text in a result.
On a single test it saves repeating a name you already gave. On a `sql_tests` [deployment rule](/monitors/deployment-rules#what-a-rule-deploys) it is the difference between a test and a copy: the rule deploys its tests onto every table the selection matches, so without the placeholder all of them run the same query against whichever table you typed.
```yaml theme={null}
- type: business_query
name: Orders with a negative total
sql_query: |
SELECT * FROM {{ table }} WHERE total < 0
```
The placeholder renders a table name, so put it where a table name goes. In a `business_query` you own the whole statement and can alias it, which is what a correlated subquery needs. In a `business_rule` the `FROM` clause is generated for you and carries no alias, so the placeholder is for a subquery of your own rather than for qualifying a column:
```yaml theme={null}
- type: business_rule
name: Order predates the first shipped order
sql_expression: "created_at < (SELECT min(created_at) FROM {{ table }} WHERE status = 'shipped')"
```
The subquery has to name the same table the test is anchored to, which is what the
placeholder is for: on a rule the test lands on every matched table, and each copy
aggregates over its own rows.
Writing `{{ table }}.column` is not portable: on BigQuery the fully qualified name is a single quoted identifier and cannot qualify a column. Reach for `business_query` when you need to name the anchor's columns.
### Evaluators
A `business_query` with no evaluators is a single verdict: every row the query returns is a failure, so the query itself has to encode what "wrong" means and the whole test is one pass or one fail. Evaluators split that in two. The query returns the numbers, and each evaluator is a named condition over those numbers with its own severity. One scan of the warehouse, several checks, each of them named in the alert.
Reach for them when one rollup answers several independent questions, when those questions deserve different severities, or when you want the alert to say which condition broke rather than how many rows came back.
#### Daily order health by channel
The query groups yesterday's orders by sales channel and computes four numbers per channel. Each evaluator reads one of them.
**SQL**
```sql theme={null}
SELECT
channel,
COUNT(*) AS order_count,
SUM(total_amount) AS revenue,
SUM(CASE WHEN status = 'refunded' THEN 1 ELSE 0 END) * 1.0
/ COUNT(*) AS refund_rate,
AVG(total_amount) AS avg_order_value
FROM orders
WHERE order_date = CURRENT_DATE - 1
GROUP BY channel
```
**Evaluators**
| Name | SQL expression | Severity |
| - | - | - |
| Revenue must not be negative | `revenue < 0` | Error |
| Refund rate above 5% | `refund_rate > 0.05` | Warning |
| Fewer than 50 orders in a channel | `order_count < 50` | Warning |
| Average order value outside the expected band | `avg_order_value < 10 OR avg_order_value > 500` | Info |
The query returns one row per channel, and every evaluator is applied to every row. A 7% refund rate on the affiliate channel flags that one row against the second evaluator and leaves the other channels alone. The test reports a warning and names the evaluator that flagged, so whoever picks up the alert knows it is refunds on one channel rather than revenue across all of them.
The same coverage without evaluators is four separate tests, each with its own `WHERE` clause and its own scan of `orders`, or one test that returns a row when anything at all is wrong and cannot say what.
#### Writing an expression
* Refer to the columns of the query's select list by the alias they were given. `refund_rate` works because the query aliased it.
* The expression is a fail condition. The row is flagged when the expression is `TRUE`, so write what you do not want to see rather than what you expect.
* `NULL` passes. A metric that can be null and should count as a failure has to say so: `revenue IS NULL OR revenue < 0`.
* Aggregate in the query and compare in the evaluator. The expression is evaluated one result row at a time, so it cannot total across rows.
* The expression is sent to your warehouse as written, in that warehouse's SQL dialect.
#### Severity
Each evaluator carries its own severity, and the test reports the worst severity among the evaluators that flagged a row.
| Worst severity that flagged | The test reports |
| - | - |
| Nothing flagged | OK |
| Info | OK |
| Warning | Warning |
| Error | Error |
An `Info` evaluator records its outcome without moving the test's status, which suits a threshold you are still calibrating or a number you want in the run history without paging anyone. A test created over the API or from a YAML configuration can leave an evaluator's severity unset, and it takes the test's own.
#### Failed rows
With **Save failures** on, the rows an evaluator flagged are written to the [audit table](#audit-table-schema), alongside how each evaluator did on that run. You can go from a failing test to the channels behind it rather than to a count.
Rows are written whenever any evaluator flags one, including when only `Info` evaluators did and the test itself reports OK. A threshold you are still calibrating therefore collects real rows before you promote it to a warning.
#### Editing evaluators
Adding, removing or changing an evaluator updates the test in place, so its history, its issues and its past runs are kept. A test on a schedule runs again straight away instead of waiting for its next slot, so the change shows up in the run view immediately.
Evaluators belong to a `business_query`, because the evaluator reads the columns the query returns and only a `business_query` gives you the whole `SELECT`.
### Audit table schema
When a test has **Save failures** on, the rows it flagged are written to an audit table in your data warehouse, so you can look at the records themselves. The table belongs to the warehouse integration rather than to any one test: set its fully qualified name in the integration's **Audit table FQN** field and Coalesce Quality creates it if it does not already exist. While that field is empty a test writes nothing, whatever its Save failures setting.
#### Naming the table
The name is yours to choose. Give the integration a fully qualified name the connection can create and write to; the placeholder in that field shows the shape your platform expects, from a bare table name on Redshift and PostgreSQL to `catalog.schema.table` on Databricks. One audit table serves every SQL test on that integration, and the statements below use `quality_sql_test__audit` as the name.
#### Schema by data warehouse
The audit table schema varies slightly by data warehouse. Below are the CREATE TABLE statements for each supported platform:
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id STRING
, sql_test_path STRING
, sql_test_name STRING
, execution_id STRING
, rows_count INTEGER
, result STRING
, evaluators STRING
, created_at TIMESTAMP
);
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id STRING
, sql_test_path STRING
, sql_test_name STRING
, execution_id STRING
, rows_count INTEGER
, result STRING
, evaluators STRING
, created_at TIMESTAMP
);
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id STRING
, sql_test_path STRING
, sql_test_name STRING
, execution_id STRING
, rows_count INT
, result STRING
, evaluators STRING
, created_at TIMESTAMP
);
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS "quality_sql_test__audit" (
integration_id TEXT
, sql_test_path TEXT
, sql_test_name TEXT
, execution_id TEXT
, rows_count INTEGER
, result TEXT
, evaluators TEXT
, created_at TIMESTAMPTZ
);
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id TEXT
, sql_test_path TEXT
, sql_test_name TEXT
, execution_id TEXT
, rows_count INT
, result TEXT
, evaluators TEXT
, created_at TIMESTAMP
);
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS "quality_sql_test__audit" (
integration_id VARCHAR
, sql_test_path VARCHAR
, sql_test_name VARCHAR
, execution_id VARCHAR
, rows_count INTEGER
, result VARCHAR
, evaluators VARCHAR
, created_at TIMESTAMP
);
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id String
, sql_test_path String
, sql_test_name String
, execution_id String
, rows_count Int32
, result String
, evaluators String
, created_at DateTime64(8, 'UTC')
) Engine = MergeTree()
ORDER BY (integration_id, sql_test_path, execution_id, created_at)
;
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id VARCHAR
, sql_test_path VARCHAR
, sql_test_name VARCHAR
, execution_id VARCHAR
, rows_count INTEGER
, result VARCHAR
, evaluators VARCHAR
, created_at TIMESTAMP
)
```
```sql theme={null}
IF NOT EXISTS (SELECT * FROM sys.tables WHERE object_id = OBJECT_ID('quality_sql_test__audit'))
CREATE TABLE quality_sql_test__audit (
integration_id NVARCHAR(MAX)
, sql_test_path NVARCHAR(MAX)
, sql_test_name NVARCHAR(MAX)
, execution_id NVARCHAR(MAX)
, rows_count INT
, result NVARCHAR(MAX)
, evaluators NVARCHAR(MAX)
, created_at DATETIME2
);
```
```sql theme={null}
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE quality_sql_test__audit (
integration_id VARCHAR2(4000)
, sql_test_path VARCHAR2(4000)
, sql_test_name VARCHAR2(4000)
, execution_id VARCHAR2(4000)
, rows_count NUMBER
, result VARCHAR2(4000)
, evaluators VARCHAR2(4000)
, created_at TIMESTAMP
)';
EXCEPTION WHEN OTHERS THEN
IF SQLCODE != -955 THEN RAISE; END IF;
END;
```
```sql theme={null}
CREATE TABLE IF NOT EXISTS quality_sql_test__audit (
integration_id VARCHAR
, sql_test_path VARCHAR
, sql_test_name VARCHAR
, execution_id VARCHAR
, rows_count INTEGER
, result VARCHAR
, evaluators VARCHAR
, created_at TIMESTAMP
);
```
#### Column descriptions
| Column | Description |
| - | - |
| `integration_id` | Unique identifier of the data warehouse integration |
| `sql_test_path` | Path or identifier of the SQL test |
| `sql_test_name` | Name of the SQL test at the time of the run |
| `execution_id` | Unique identifier for each test execution |
| `rows_count` | Number of rows that failed the test |
| `result` | The failed records, as JSON |
| `evaluators` | Per-evaluator outcomes for a `business_query` test, as JSON — each entry carries the evaluator's `id`, `name`, `sql_expression`, `severity`, `fail_count`, `status` and `message`, snapshotted as they were at execution time. Empty for every other test type |
| `created_at` | Timestamp when the audit record was created |
The table is created when you save the integration with an **Audit table FQN** set, so the connection needs permission to create tables in that schema or database. Without the privilege the integration reports the failure on its status; you can also create the table yourself with the statement above and grant `INSERT` on it instead.
An audit table is created once and is not altered afterwards, so a table that predates
the `sql_test_name` and `evaluators` columns keeps its original shape and those two
values are dropped from the rows written to it. Add the two columns yourself to start
recording them.
# Working with time dimensions and scheduling for monitors
Source: https://docs.synq.io/monitors/time-dimensions
When defining scheduled monitors, you can control **when** and **how** your data is evaluated through *time partitioning* and *scheduling*. These settings determine both the time window a monitor measures (e.g. “yesterday’s data”) and when the measurement occurs (e.g. “4 AM New York time”).
This guide explains how scheduling and time partitioning interact, the available configurations, and how to visualize them using the [Partition Timeline Tool](https://app.synq.io/partition-timeline-test).
## Understanding time partitioning
Every monitor requires a **time partition field**, such as `created_at` or `date`. This field defines how data is grouped over time and is used to determine which records belong to a given measurement window.
You can then choose how frequently to measure the data — daily, hourly, or down to minutes — and optionally define a delay to account for late-arriving data.
## Scheduling options
Each monitor run is defined by three parameters:
1. **Frequency** – how often the monitor runs (e.g. daily, hourly).
2. **Partition time** – the timestamp field used to group data.
3. **Time zone and offset** – when in the day the monitor executes.
By default, all monitors execute internally in **UTC** for consistency and easier debugging. You can still define schedules in your local time zone — the platform automatically converts this to the correct UTC schedule under the hood.
### Common scheduling examples
* **Daily at midnight UTC**
Measures data for the previous calendar day.
* **Daily at 2 AM UTC**
Measures data for the previous calendar day at 2AM (useful for delayed ETL jobs).
* **Daily at midnight New York**
Runs at 5 AM UTC but measures midnight → midnight in the New York time zone.
These combinations allow teams to align monitoring with pipeline completion times and business-day boundaries.
## Handling data delays
Many teams have ETL pipelines that complete several hours after midnight. In these cases, you can configure monitors to **delay execution** (for example, run at 4 AM but still measure the previous day’s partition).
This ensures freshness or volume checks evaluate complete data instead of partially loaded partitions.
## Advanced configurations
The platform supports advanced scheduling behaviors to cover all data-readiness scenarios:
* **Ignore last partition** – Skip the most recent day/hour if data isn’t yet ready (useful for delayed ingestion).
These options allow you to model both CRON-style schedules and dynamic, event-driven monitoring.
## Visualizing your configuration
You can preview how your scheduling and partitioning settings interact using the interactive partition timeline tool below. It helps you confirm exactly what data window each run will measure and when it will execute.
# Airflow
Source: https://docs.synq.io/orchestration-integrations/airflow
Integrating Airflow with Coalesce Quality
This guide will show you how to securely connect your Airflow setup to Coalesce Quality.
This integration extracts metadata about Airflow. By default, all tasks and all DAGs are reported.
To be able to finish this guide, you’ll need the following:
→ Access to modify your Airflow configuration code
⏱️ Estimated time to finish: 10 minutes.
## Setup with OpenLineage
OpenLineage is the recommended setup. It uses Airflow's own OpenLineage provider.
1. In the app, create a new Airflow integration with `OpenLineage` as the processing mode.
2. Create an API token on the integration's page, and use it to configure Airflow to forward your events to the ingestion service.
Airflow uses a `transport` configuration option to define how to forward these events.
* **URL**: `https://developer.synq.io`. For other regions use `https://api.us.synq.io` (US) or `https://api.au.synq.io` (AU).
* **Endpoint**: `/api/ingest/openlineage/v1`
* **API Key**: the API token from step 2.
The integration's page in the app shows the URL for your region and the matching transport configuration.
With that information, you can follow the [Airflow OpenLineage documentation](https://airflow.apache.org/docs/apache-airflow-providers-openlineage/stable/index.html) to configure your Airflow instance.
### Example OpenLineage Transport Value
```json theme={null}
{
"type": "http",
"url": "https://developer.synq.io",
"endpoint": "/api/ingest/openlineage/v1",
"auth": {
"type": "api_key",
"api_key": "st-***" // redacted
}
}
```
## Setup with DataHub
If you already run the DataHub Airflow plugin, you can send its metadata instead. Create the Airflow integration with `DataHub` as the processing mode.
1. Install the required dependencies in your Airflow
```
pip install 'acryl-datahub-airflow-plugin>=1.1.0.4' apache-airflow-providers-openlineage
```
For detailed installation instructions and compatibility information, see the [DataHub Airflow documentation](https://docs.datahub.com/docs/lineage/airflow).
2. Create an API token on the Airflow integration's page in the app.
3. Set up the REST hook
* `conn-host`: `https://developer.synq.io/api/ingest/airflow/v1`. For other regions use `https://api.us.synq.io/api/ingest/airflow/v1` (US) or `https://api.au.synq.io/api/ingest/airflow/v1` (AU).
* `conn-password`: the API token from step 2
```
airflow connections add 'datahub_rest_default' --conn-type 'datahub-rest' --conn-host '' --conn-password ''
```
The integration's page in the app shows the host for your region and this command.
### Airflow 2.7+ with OpenLineage Provider
If you're using Airflow 2.7+, the native Airflow OpenLineage provider will improve the quality of lineage and metadata information obtained from your Airflow setup. The OpenLineage provider package is already included in the installation above since the DataHub plugin requires it.
For AWS MWAA, add both `acryl-datahub-airflow-plugin` and `apache-airflow-providers-openlineage` to your `requirements.txt` file.
## Log Forwarding
Log forwarding is required to include full task failure logs in alerts. Multiple methods are supported:
### AWS MWAA CloudWatch Logs
For AWS Managed Workflows for Apache Airflow (MWAA), you can forward logs from CloudWatch using the `synq-aws-cloudwatch` Lambda function:
1. **Deploy the Lambda function**: Use the [synq-aws-cloudwatch](https://github.com/getsynq/synq-aws-cloudwatch) repository to deploy a Lambda function that forwards CloudWatch logs to Coalesce Quality.
2. **Configure log forwarding**: The Lambda automatically forwards Airflow logs from CloudWatch when properly configured with your credentials.
3. **Set up log group subscription**: Configure CloudWatch to trigger the Lambda when new Airflow logs are available.
### API Endpoint
You can also send logs directly using the [API endpoint](/api-reference/synqingestairflowv1airflowlogsservice/ingestlog). This endpoint accepts Airflow log data for processing and analysis.
### Remote Logging (S3/GCS)
You can also upload logs after files are created on S3 or GCS using Airflow's Remote Logging feature. For assistance with this setup, please reach out to our support team.
## Read more
For more information about the plugins used to collect task execution and lineage data:
* [DataHub documentation on the Airflow plugin](https://datahubproject.io/docs/lineage/airflow/)
* [DataHub source code](https://github.com/datahub-project/datahub/blob/master/metadata-ingestion/src/datahub_provider/client/airflow_generator.py)
* [OpenLineage documentation](https://openlineage.io/docs/)
* [Airflow OpenLineage source code](https://github.com/apache/airflow/tree/main/providers/openlineage)
# Airflow + dbt Core Linking
Source: https://docs.synq.io/orchestration-integrations/airflow-dbt-core
Link dbt models to Airflow tasks and view orchestration context
This guide explains how Coalesce Quality automatically links your dbt models with the Airflow tasks that execute them, providing end-to-end visibility from orchestration to data models.
**Prerequisites:**
* [Airflow integration](/orchestration-integrations/airflow) configured
* [dbt Core integration](/dbt-integrations/dbt-core) with `synq-dbt` v2.0.0+ installed
* `synq-dbt` running within Airflow tasks
* Valid integration token
⏱️ **Estimated time:** Already works automatically if prerequisites are met
# How it works
When you run dbt Core commands using `synq-dbt` inside Airflow tasks, the Airflow execution context is automatically captured and linked to your dbt models. This creates a bidirectional connection between your orchestration layer and data models.
**What you get:**
* **Model → Airflow**: See which Airflow tasks executed each dbt model, with DAG and task information on model pages
* **Airflow → Models**: View all dbt models executed by an Airflow task, with run status and metrics
* Visualize orchestration in the lineage view (Orchestration mode)
* Historical tracking of model executions (last 30 days)
* **Airflow context in issues**: When a model fails, the specific Airflow DAG and task information is included in the issue, making it easier to identify and troubleshoot failures
# Automatic context collection
When `synq-dbt` uploads your dbt artifacts (`run_results.json`), it automatically detects and collects Airflow-specific environment variables:
**Required variables** (for linking to work):
* `AIRFLOW_CTX_DAG_ID` — DAG identifier
* `AIRFLOW_CTX_TASK_ID` — Task identifier
* `AIRFLOW_CTX_DAG_RUN_ID` — Unique run identifier
* `AIRFLOW_CTX_TRY_NUMBER` — Retry attempt number
**Optional variables** (enhance tracking and context):
* `AIRFLOW_CTX_DAG_OWNER` — DAG owner
* `AIRFLOW_CTX_EXECUTION_DATE` — Execution timestamp
These environment variables are automatically set by Airflow in your task execution context. You don't need to manually configure them — they're available in the task environment. However, when using operators like `KubernetesPodOperator`, you need to explicitly pass them using Jinja templating as shown in the setup examples below.
# Setup verification
If you've already set up both integrations, the linking is already active. To verify:
## 1. Check your Airflow setup
Ensure you're using `synq-dbt` to execute dbt commands in your Airflow tasks:
**Example with KubernetesPodOperator:**
```python theme={null}
KubernetesPodOperator(
env_vars={
"SYNQ_TOKEN": Variable.get("SYNQ_TOKEN"),
# Required: Airflow context for linking
"AIRFLOW_CTX_DAG_ID": "{{ dag.dag_id }}",
"AIRFLOW_CTX_TASK_ID": "{{ task.task_id }}",
"AIRFLOW_CTX_DAG_RUN_ID": "{{ dag_run.run_id }}",
"AIRFLOW_CTX_TRY_NUMBER": "{{ task_instance.try_number }}",
# Optional: Additional context
"AIRFLOW_CTX_EXECUTION_DATE": "{{ execution_date }}",
"AIRFLOW_CTX_DAG_OWNER": "{{ dag.owner }}",
# Non-EU workspaces only:
# "SYNQ_API_ENDPOINT": "https://api.us.synq.io" # or https://api.au.synq.io
},
cmds=["synq-dbt"],
arguments=["build"],
# ... other configuration
)
```
**Regional Configuration:** EU region customers (default) don't need to set `SYNQ_API_ENDPOINT`. US and AU region customers must add it to `env_vars` — `"https://api.us.synq.io"` or `"https://api.au.synq.io"`.
**Example with DbtRunOperator:**
```python theme={null}
dbt_run = DbtRunOperator(
dbt_bin='synq-dbt',
env={
"SYNQ_TOKEN": Variable.get("SYNQ_TOKEN"),
# Required: Airflow context for linking
"AIRFLOW_CTX_DAG_ID": "{{ dag.dag_id }}",
"AIRFLOW_CTX_TASK_ID": "{{ task.task_id }}",
"AIRFLOW_CTX_DAG_RUN_ID": "{{ dag_run.run_id }}",
"AIRFLOW_CTX_TRY_NUMBER": "{{ task_instance.try_number }}",
# Optional: Additional context
"AIRFLOW_CTX_EXECUTION_DATE": "{{ execution_date }}",
"AIRFLOW_CTX_DAG_OWNER": "{{ dag.owner }}",
# US: "SYNQ_API_ENDPOINT": "https://api.us.synq.io"; AU: "https://api.au.synq.io"
},
# ... other configuration
)
```
## 2. Run a dbt task in Airflow
Execute any DAG that runs dbt models using `synq-dbt`.
Artifacts and linking data typically appear within minutes of upload under normal conditions. Wait 5-10 minutes after your first execution for metadata to sync before checking.
## 3. View linked data
### On model pages
Navigate to any dbt model page to see:
* **Airflow Execution History**: Recent task executions that ran this model
* **DAG Information**: Which DAG and task executed the model
* **Execution Timestamps**: When the model was last executed via Airflow
### On Airflow execution pages
Navigate to any Airflow task execution page to see:
* **Executed dbt Models**: Complete list of all dbt models that ran in this task
* **Model Run Status**: Success/failure status for each model
* **Execution Details**: Runtime, rows affected, and other metrics per model
* **Direct Links**: Quick navigation to individual model pages
### In lineage view
1. Open **Lineage**
2. Switch to **Orchestration mode** (toggle in the view settings)
3. View your complete data pipeline including:
* Airflow DAGs and tasks
* dbt models executed by each task
* Connections between orchestration and data layers
# Airflow context in issues
When a dbt model fails during execution in an Airflow task, the issue is automatically enriched with Airflow execution context. This helps you quickly identify which specific DAG and task encountered the failure.
**Information included:**
* **DAG ID**: The Airflow DAG that was running
* **Task ID**: The specific task within the DAG that executed the failed model
* **Execution Date**: When the failure occurred
* **DAG Run ID**: Unique identifier for the DAG run
* **Owner**: DAG owner for faster escalation
This context appears directly in the issue details, allowing you to:
* Jump directly to the relevant Airflow task logs
* Identify if the failure is specific to a particular orchestration path
* Correlate model failures with DAG execution patterns
* Route issues more effectively based on DAG ownership
The Airflow context is captured at the time of execution and remains associated with the issue, even if you investigate it later.
# Troubleshooting
## Models not showing Airflow context
**Check the following:**
1. **Using synq-dbt**: Verify you're running `synq-dbt` instead of `dbt` in your Airflow tasks
2. **synq-dbt version**: Ensure you're using v2.0.0 or later (run `synq-dbt --version`)
3. **Environment variables passed**: Verify that required Airflow context variables (`AIRFLOW_CTX_DAG_ID`, `AIRFLOW_CTX_TASK_ID`, `AIRFLOW_CTX_DAG_RUN_ID`) are being passed to your operator
4. **Regional endpoint**: US region customers must set `SYNQ_API_ENDPOINT=https://api.us.synq.io`, AU customers `https://api.au.synq.io`
5. **Recent execution**: Linking shows executions from the last 30 days
6. **Airflow version**: Ensure your Airflow version sets the context variables (supported in Airflow 1.10+)
7. **Upload success**: Check that `synq-dbt` successfully uploaded artifacts (check task logs for upload confirmation)
8. **Network connectivity**: Verify outbound HTTPS access to `developer.synq.io:443` (EU), `api.us.synq.io:443` (US) or `api.au.synq.io:443` (AU)
## Lineage not showing orchestration
1. **Orchestration mode enabled**: In Lineage view, ensure "Orchestration mode" is toggled on
2. **Airflow integration active**: Verify your [Airflow integration](/orchestration-integrations/airflow) is properly configured
3. **Metadata sync**: Wait 5-10 minutes after first execution for metadata to sync
# Learn more
* [Airflow integration guide](/orchestration-integrations/airflow) — Connect Airflow to Coalesce Quality
* [dbt Core integration guide](/dbt-integrations/dbt-core) — Install and configure synq-dbt
* [synq-dbt GitHub repository](https://github.com/getsynq/synq-dbt) — Source code and advanced configuration
# Creating an owner
Source: https://docs.synq.io/ownership/creating-an-owner
### Create an owner
1. Navigate to `Owners` and `Create an owner`
2. Name the owner (e.g., `product analytics`)
### Link ownership with Slack
Linking an owner to Slack enables you to send notifications to a dedicated Slack channel and tag the relevant owner(s) in Slack based on their Slack handle.
1. Click `@Add Slack`
1. If it’s your first time setting up Slack, click `Connect to Slack` to grant the necessary permissions. See more in [Slack integration](/alert-integrations/slack)
2. Select a Slack channel from the drop-down list to send notifications.
3. Select one or multiple Slack user handles or Slack user groups to be notified of issues on assets owned by them (optional).
If you want to send alerts to a private Slack channel, you must first type `@Coalesce Quality` in the private channel and invite the app.
### Link ownership with email
Linking an owner with one or multiple email addresses lets you notify the relevant owner(s) via email.
1. Click `Add Email` to link an owner to an email address.
2. Click `Add Another email` to link an owner to another email address (optional).
### Add assets to an owner
There are three ways to assign assets to an owner: (1) Data product, (2) Custom query, and (3) All assets.
1. Click `Add owned assets` and select one of the three options to add assets to the owner.
1. Data product—use this to attach an existing data product to the owner
2. Custom query—use this to specify assets using the browser or one of the other filter options
3. All assets—use this if you want to be notified of any issue in your workspace (e.g. if the owner is `data team`)
### Set alerts for an owner (optional)
By setting alerts on an owner, the owner will automatically be notified in the specified Slack channel or email address(es) you specified
1. To enable alerts for the assets you selected for the owner, toggle `Setup alerts`
2. Set the options based on your preference and click `continue` to set up the owner
### Managing owners programmatically
An owner, its notification channels and the alert configuration on each of its
ownerships are all readable and writable outside the app, so alert routing can be
reviewed and changed in bulk.
* **Public API** — `synq.owners.v1.OwnersService` covers owners, their contacts
and their ownerships. Reading needs a token with `SCOPE_OWNERS_READ` and
`SCOPE_OWNERSHIP_READ`; writing needs `SCOPE_OWNERS_EDIT` and
`SCOPE_OWNERSHIP_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [owner tools](/scout/mcp#owners-and-ownership) create or update
an owner and the assets it is responsible for in one call, including the
severities and channels its alerts go to. Reading is covered by read-only
consent; writing needs the Governance permission and is
confirmation-gated.
# Domains
Source: https://docs.synq.io/ownership/domains
Domains help you organize your data assets by letting people see only the data they should care about. Domains can be set to fit existing definitions (e.g., using metadata from dbt, SQLMesh, or Coalesce Transform), or can have custom definitions built with the UI drop-down menu.
*Quickly filter down key data assets by domain to see what matters to you. Members of your workspace can only see data that belongs to domains they’re in, helping you keep things simple as you scale.*
### Setting up a Domain
1. Navigate to `Settings` → `Domains` and click `Create`
2. Give your Domain a name and a description
3. Click the newly created domain and select data assets that belong to the domain. This can be Data Products or individual data assets
4. Add members to the domain by clicking `Add Member`. These members will belong to the domain and be able to see all the data assets you selected above. The member must already exist in the workspace for you to be able to add them.
### Working with subdomains
Subdomains are helpful if you have larger domains (e.g., Marketing) with clearly distinguishable areas (e.g., performance marketing, brand marketing).
To set up one or more subdomains:
1. Navigate to a domain
2. Click `Add sub-domain`
3. Name your subdomain, and select relevant assets and members.
### Member roles in domains
* Admin users can toggle between all domains, as well as see data across domains using the default `ALL` domain selector.
* All other user roles will only see data for domains they belong to. Read more about [user roles](https://docs.synq.io/admin/user_roles). In cases where no domain has been set, all users will be able to see all data.
You can select a member in the workspace, and click the three dots to set a domain as their default, which will make it their default data access each time they open the platform.
### Managing domains programmatically
Domains, their nesting and their membership can be managed outside the app.
* **Public API** — `synq.domains.v2.DomainsService` lists domains, walks the
hierarchy, resolves the assets in one, and upserts or deletes it. Reading needs
a token with `SCOPE_DOMAIN_READ`, writing needs `SCOPE_DOMAIN_EDIT`. See the [API reference](/api-reference/api) and [API scopes](/api-reference/scopes).
* **MCP** — the [domain tools](/scout/mcp#domains) let an AI assistant walk the
hierarchy, draft a membership definition combining pinned assets, data products
and other domains, and save it once you approve. Reading is covered by
read-only consent; saving and deleting need the Governance permission and are
confirmation-gated.
### How does the domain impact access to data
Domains provide a limited view into the data ecosystem for a user. While the limits are intended to help them focus on the data they are interested in, they will still be able to access any data asset, including the lineage in and out of their domain of interest. Domains are not meant to implement data access controls. We recommend that these controls should be put directly in your data storage (e.g., your data warehouse).
# Ownership overview
Source: https://docs.synq.io/ownership/ownership-overview
Owners clarify ownership of data assets and active workflows that help owners stay informed about the health and status of their data
Ownership can be set up both within and outside the data team to, e.g., notify an upstream engineering team of issues on a data source created by them.
Defining ownership lets you.
* Group related data assets and their health by owner group to make it easier to take action.
* Alert or tag the relevant people in Slack based on the status of assets they own.
* Assign owners to a data product to foster a sense of responsibility within the data stack.
*An overview of example owners and their associated ownerships. You can create an unlimited number of owners*
### Create an owner
1. See [Creating an owner](/ownership/creating-an-owner)
### Editing or deleting an owner
1. Navigate to `Owners` and click the three dots to `Edit` or `Delete` an owner
### Ownership best practices
While there’s no one-fit-answer to ownership, we’ve seen these approaches work well.
[We’re always up for a chat](/support/support) if you want advice on setting up ownership for your specific situation.
* **Start small and don’t overthink it**—if you’re just getting started, you don’t have to define owners for all your data assets. Instead, start by defining the smallest possible amount of data assets that are most critical for your business. Data Products ([Data products overview](/data-products/data-products-overview)) is a good starting point.
* **Beware of individual ownership**—use existing groups to define ownership that already represents how your company works. Beware of defining owners as individuals (e.g., `John Doe`). Instead, use a group that represents their function (e.g., `product analytics`) even if there’s only one person in the group. This makes it easier to manage if people move around or leave your company.
* **Get buy-in outside the data team**—before you start routing alerts to upstream teams, ensure that you clearly agree on what’s expected when they receive an alert.
* **Integrate ownership into your existing workflows**—for example, integrating ownership into your Slack or email alerting is a great way to drive accountability and see action from the work you put into defining owners.
* **Be consistent**—be consistent in how you define ownership and at what level so different teams don’t make their own interpretations.
# Agent workflow
Source: https://docs.synq.io/reconciliation/agent-workflow
The operating guide for a coding agent driving the synq-recon CLI
This page is the published copy of the operating guide that ships with the tool
as `AGENTS.md` — in the release archive, in
[getsynq/synq-recon](https://github.com/getsynq/synq-recon), and at
`/opt/synq-recon/AGENTS.md` inside the Docker image. It is deliberately denser
than the rest of this section. Start at
[Getting started](/reconciliation/getting-started) if you want the guided
version, and see the [CLI reference](/reconciliation/cli) for every flag.
This is the operating guide for a coding agent using `synq-recon` to author,
validate, run, promote and investigate a reconciliation suite: the loop to
follow, the mistakes that cost real time, how to read the output, and what each
command spends. It is written to be acted on top-down.
A **reconciliation** compares one dataset in a source database against one in a
target database and reports whether they agree. A **suite** is a YAML file
holding several reconciliations plus the connections they use. Only counts and
checksums leave the database by default — no row values.
## 1. The loop
Work through these in order. Each step has a condition for moving on; do not
skip ahead, because every later step is more expensive than the one before it.
**1. Validate offline — `check-config suite.yaml`**
Parses the YAML, checks required fields, and warns about time-dependent SQL.
It shows all the errors that it finds. Costs nothing, touches no
database.
> Move on when it prints `Configuration check passed!`. Fix errors here rather
> than discovering them mid-run. Warnings about `NOW()` / `CURRENT_DATE` are
> real — see [Never do these](#2-never-do-these).
**2. Read what it will do — `plan suite.yaml`**
Prints the resolved execution plan: every reconciliation the run covers, which
connection each side uses, and the exact SQL both sides will execute after
template variables, table references and time-travel snapshots resolve. Costs
nothing, touches no database.
> Move on when the quoted SQL is the SQL you meant to run. This is the cheapest
> place to catch a variable that resolved to the wrong date or a table reference
> pointing at the wrong schema — both of which otherwise surface as a confusing
> `MISMATCH` after you have paid for the scan.
>
> Values a run derives from live data — a cutoff watermark, the boundaries a
> drill picks — are named but not resolved; deriving them needs a query.
**3. Validate against the databases — `check-config suite.yaml --db`**
Connects to every connection, runs each query through the database's query
planner (`LIMIT 0`), and reports the resolved columns. It also reports table
size, primary/partition keys, a suggested key column, and warns when the key
column you chose is not indexed.
> Move on when every connection is `OK` and every query validates. A failure
> here is a wrong table name, a missing grant, or a column that does not exist —
> all of which would otherwise surface as a confusing mid-run error.
>
> `--db` does **not** run the suite's `setup:` SQL, so a suite whose tables are
> created by `setup` will report every query as failed. That is expected; skip
> to step 4 for such suites.
**4. Compare — `run-check suite.yaml`**
Each reconciliation's own comparison, without a drill: the row count for
`row_count`, the row count and checksum for `row_checksum` (one query per side,
fast and cheap regardless of table size), and the grouped measures for
`aggregate`.
> If it reports `MATCH`, the datasets agree and you are done.
> If it reports `MISMATCH`, go to step 5. Exit code 1 means "differences found",
> which is a result, not a failure.
**5. Locate — `run suite.yaml --auto-drill`**
Runs the quick check, then bisects the key space on whatever mismatched to
narrow the difference to specific key ranges. `run-drill` does the same without
re-running the quick check.
> Move on when the drill reports mismatch leaves with usable key ranges. If it
> reports thousands of leaves, stop and read
> [Never do these](#2-never-do-these) — you are drilling something that should
> be localised with an aggregate comparison first.
**6. Save to the workspace — `upload-config suite.yaml`**
Stores and versions the suite in your Coalesce Quality workspace, where it
appears under **Development**. Requires `SCOPE_RECON_EDIT`.
> Run `auth whoami` first. Move on when it prints the suite id.
**7. Run it on the backend — `run-remote --wait`**
Executes the suite in Coalesce Quality against workspace integrations rather
than against databases this machine can reach. Connections bind to integrations
by name; override with `--map connection=integration_id`.
> Move on when the run completes. `--wait` exits `0` if everything passed, `1`
> if a `--fail-on` condition was hit, `2` if execution itself failed.
> **Pass `--drill=false` explicitly if you do not want a drill** — omitting the
> flag does not mean "off".
**8. Publish to production — `promote `**
Creates a deployment: an immutable snapshot of the suite plus an optional
schedule, so it runs on its own and its results become platform assets, checks
and issues. Requires `SCOPE_RECON_PROMOTE`.
> A deployment is a snapshot, not a pointer: editing the suite afterwards
> changes nothing in production until you re-promote. Re-promoting preserves
> settings you omit; a *fresh* promote applies defaults, and the drill default
> on a fresh promote is **on**.
>
> Run `promote --diff` with the same flags first — it shows what
> would change in production and promotes nothing.
**Any time after a run:** `recheck ` re-executes what it ran and reports
what moved; `drill-deeper ` continues its drill from where it stopped. Both
take a local audit-log file or the invocation id of a stored run. See
[Investigating a finished run](#7-investigating-a-finished-run).
***
## 2. Never do these
Each of these has already cost someone real time.
**Do not hand-query the warehouse to "check something first."** Express the
question as a reconciliation and let `synq-recon` run it — that way the result
is recorded, reproducible, and comparable to the next run. If the tool cannot
express what you need, that is a bug worth reporting, not a reason to open a SQL
client.
**Do not commit `.connections.yaml`, or any credential, ever.** Keep the real
file git-ignored and commit only a `.connections.yaml.example` whose values are
`${ENV_VAR}` placeholders. A suite YAML with a literal secret in it is also a
suite you cannot safely `upload-config`.
**Do not run a threshold-1 drill over a table with tens of thousands of genuine
differences.** Bisection is O(log n) when the data is *mostly* identical; when
most rows differ it cannot halve, and it degenerates into thousands of queries
that each rescan a large slice of the table. Localise first: an aggregate
comparison (`mode: aggregate`, a `COUNT` measure grouped by the natural
partition) tells you *which* groups diverge in roughly one query per side. Then
drill a query narrowed to those groups.
**Do not omit `--drill` on `run-remote` and expect no drill.** Unlike `promote`,
where an omitted flag preserves the stored setting, an ad-hoc run has no
deployment to inherit from, so an omitted `--drill` drills every mismatch whose
reconciliation has `bisection.enabled` true, which is the default. Pass
`--drill=false`. The reverse does not hold: `--drill` never drills a
reconciliation with `bisection.enabled: false`, and neither does
`run --auto-drill`; only `run-drill` and `drill-deeper` do.
**Do not put `NOW()`, `CURRENT_DATE` or `GETDATE()` in a query.** They evaluate
at slightly different instants on the two sides, which manufactures differences
that are not there. Use a template variable, resolved once and interpolated into
both sides:
```yaml theme={null}
variables:
start_date: "{{ today - 7d }}"
end_date: "{{ today }}"
```
`check-config` warns about this; `strict_time_references: true` makes it an
error.
**Do not synthesise a concatenated key.** For a multi-column natural key, declare
`key_columns: [workspace, path]`. Bisection then orders and range-filters on the
column *tuple*, so a matching primary key or index still prunes each segment. A
synthetic `a || '|' || b` key cannot use the index and forces a full scan per
segment.
**Do not compare a lagging replica without a cutoff.** If the target trails the
source, rows still in flight look like missing rows. A `cutoff:` derives a
watermark from the data itself and filters both sides to at-or-below it. See
[Cutoffs](#avoiding-false-differences).
***
## 3. Confirm the target before any mutation
Every command that writes to a workspace — `upload-config`, `promote`,
`unpromote`, `trigger`, `run-remote`, `deployment …`, `suite delete`,
`runs cancel` — acts on whichever workspace your credential resolves to.
```bash theme={null}
synq-recon auth whoami
```
Read the `workspace:` line and confirm it is the one you mean **before** the
mutation, not after. There is deliberately no `--workspace` override: your
credential determines the target, so the way to change it is to change the
credential.
Credentials resolve in a fixed order, so an automated pipeline never silently
picks up a developer's browser login:
1. Client credentials — `--client-id` / `--client-secret`, or
`QUALITY_CLIENT_ID` / `QUALITY_CLIENT_SECRET`, or the `synq:` block in the suite.
2. An API token — `QUALITY_TOKEN`.
3. The browser login cached by `synq-recon auth login`, refreshed automatically.
`--region eu|us|au` selects the deployment, and `--endpoint` (or
`QUALITY_API_ENDPOINT`) points at a staging or self-hosted one. Both are root
flags, so they work on every command. Whichever you use also selects *which
region's* stored credential is used — a token minted for one region is never
sent to another.
You rarely need either: `synq-recon auth login --region au` records the
deployment, and later commands resolve to it. `synq-recon auth use `
switches between deployments you are already logged in to, and
`auth use --clear` goes back to the default (`eu`). Resolution order is
`--endpoint` > `--region` > the suite's `synq.endpoint` > `QUALITY_API_ENDPOINT`
> `QUALITY_REGION` > the last login > `eu`.
The `--synq-`-prefixed flags and `SYNQ_`-prefixed variables are the previous
spellings and still work.
Permissions: `SCOPE_RECON_READ` to inspect, `SCOPE_RECON_EDIT` for
`upload-config`, suite edits and reporting a locally executed run's results,
`SCOPE_RECON_PROMOTE` to promote or trigger. All three can be granted to a
workspace API client, so every command works unattended.
***
## 4. Authoring a suite
The smallest useful suite is a connection pair and one reconciliation:
```yaml theme={null}
name: orders-consistency
title: "Orders consistency"
connections:
gateway:
postgres:
host: db.internal
port: 5432
database: payments
username: ${PG_USER}
password: ${PG_PASSWORD}
warehouse:
snowflake:
account: myorg.us-east-1
warehouse: COMPUTE_WH
role: ANALYST
username: ${SF_USER}
password: ${SF_PASSWORD}
databases: [ANALYTICS]
reconciliations:
orders-daily:
source:
connection: gateway
table: public.orders
target:
connection: warehouse
table: ANALYTICS.PUBLIC.ORDERS
key_columns: [order_id]
mode: row_checksum
```
Points that decide whether it works:
* **`table:` beats `query:` when you can use it.** With `table:` the tool
resolves the columns for you and you can narrow with `columns:`,
`exclude_columns:` and `where:`. Reach for `query:` only when the comparison
genuinely needs SQL. A `query:` side takes `columns:` and `exclude_columns:`
too, to compare part of what the query returns; a cutoff still filters the
query as written, so its column need not be among them. `where:` is for a
`table:` only, and a suite with one beside a `query:` does not load: put the
filter in the query.
* **Key columns drive everything.** Bisection orders and range-filters on them,
so they must be unique and, ideally, indexed. `check-config --db` suggests one
and warns if yours is not indexed. Keep them among a side's `columns:` and out
of its `exclude_columns:`; the suite warns when a side leaves one out, because
a drill there fails as soon as it has a difference to locate.
* **Modes.** `row_count` compares counts only (fastest, misses modified values,
and takes a tolerance in `row_count.thresholds` for a replica that lags);
`row_checksum` compares counts and a checksum of every column (the default,
catches any difference); `aggregate` compares grouped measures like `SUM` and
`COUNT` with a tolerance, and drills down through a `group_columns` hierarchy.
With no `group_columns`, it groups by the key columns; with no key columns
either, it compares the whole dataset as one group.
`full` is an accepted legacy spelling of `row_checksum`; write `row_checksum`.
* **The two sides need the same number of columns — except in `row_count`.** The
checksum modes hash the compared columns position by position, so a wider
target fails with `source has N columns but target has M columns`. Narrow both
sides with `columns:`, or drop the extras with `exclude_columns:` on the wider
side. `row_count` reads no column at all and compares whatever the two sides
are, so a target with extra columns needs no column list; only the key columns
have to exist on both sides.
* **Column names differing only in case match automatically.** For genuinely
different names use `column_mapping`.
* **Write names the way you would write them unquoted; quote only to pin one.**
Every part of a `table:` and every entry of a side's `columns:` is quoted for the
warehouse it runs on, so a table or column named after a reserved word
(`order`, `group`) works as written. An unquoted name resolves the way the
warehouse resolves an unquoted reference, `orders` is `ORDERS` on Snowflake
and `orders` on Postgres. To name an object created with quotes, a
lower-case Snowflake table or a name with a dot in it, quote that part
yourself: `table: 'DB.SCH."events"'`. The outer single quotes are YAML's,
because a value starting with `"` is not a plain string. Column names in
`key_columns`, `column_mapping` and `cutoff:` are looked up in what the
warehouse reports, ignoring case, so `last_updated_date_time` finds a
Snowflake column `LAST_UPDATED_DATE_TIME`.
A `cutoff.offset` or `window.lookback` is a whole number and one of `s`, `m`,
`h`, `d`, `w`, or a Go duration such as `1h30m`. An offset is signed and
usually negative (`-15m`, `-900s`); a lookback is how far back to look, so it
is positive (`14d`) — a zero or negative one is ignored, with a warning.
* **Keep credentials out of the suite.** Put them in a git-ignored
`.connections.yaml` (auto-discovered, or passed with `--connections`) keyed by
the same connection names. This is also what lets the same suite run locally
and in the workspace, where the names bind to integrations instead.
### Finding the physical table path
Both sides of a reconciliation are named by their physical
`database.schema.table`, and on a warehouse whose naming you do not control
that is the part you may not know. If the warehouse is connected to your
workspace as an integration, ask the catalog:
```bash theme={null}
# By name — description and column text are searched too
synq-recon entities search orders --type snowflake_table
# By a name you already have, to find which warehouse it is in
synq-recon entities resolve ANALYTICS.PUBLIC.ORDERS
```
Both print the SQL-addressable `database`, `schema` and `table` — exactly what
goes into a `table:` block — plus the dialect and connection id, which is how
you tell apart the several warehouses one `db.schema.table` can legitimately
match.
**This is a convenience and never a prerequisite.** The catalog holds only
warehouses connected to the workspace as integrations, so a database you
reconcile locally — a DuckDB file, a container, anything reachable from this
machine but never integrated — will not appear, and that says nothing about
whether a suite naming it runs. Nothing else in this tool consults the catalog:
`check-config`, `plan` and every run command work entirely from the suite and
its connections file, with or without a workspace.
The exhaustive field list is the published schema, at a stable versioned URL:
* [Configuration reference](https://schemas.synq.io/synq-recon/v1/config.html) —
every field, type, constraint and default, rendered.
* [`config.schema.json`](https://schemas.synq.io/synq-recon/v1/config.schema.json)
— the machine-readable form. Point your editor at it for completion and
validation:
```yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-recon/v1/config.schema.json
```
One field there is not yours to set: `motherduck_account` appears under the
DuckDB connection because the loader populates it internally. Write `database:`
with the MotherDuck account name, and pass `motherduck_token`.
### Avoiding false differences
Comparing two live systems is a moving target, and most "the data is wrong"
findings are really a comparison window problem. Reach for these first.
| Symptom | Use | What it does |
| - | - | - |
| Target lags the source; the tail looks "missing" | `cutoff:` | Derives a watermark from the data on each side, combines them (`min` by default — the point both sides have certainly reached), optionally truncates and offsets it, and filters both sides to at-or-below it. |
| Only the recent window matters | `window:` | Restricts the comparison to a lookback period: with a `column`, each table side is filtered on it; in a query, reference `{{window_start}}` / `{{window_end}}`. A cutoff on top of it reads its watermark from inside the window. |
| Queries need a date or an id boundary | `variables:` | Resolved once, interpolated into both sides, recorded in the audit log. Override per run with `--var key=value`. |
| Comparing against a point-in-time snapshot | `as_of:` | Time-travel query on platforms that support it. |
| Numbers differ only past some decimal place (a loader rounds, a `FLOAT` sits next to a `DECIMAL`) | `comparison:` | `numeric_scale: ` rounds both sides to that many decimal places before hashing. Set it on the suite, on a reconciliation, or per column under `columns:`. |
| Timestamps differ only past some fraction of a second (a loader cut them to the second) | `comparison:` | `timestamp_precision: second`, `millisecond` or `microsecond` cuts both sides to it, key columns included. |
| Timestamps without a time zone are off by whole hours (a loader moved local wall clocks to UTC) | `comparison:` | `source_time_zone:` / `target_time_zone:` name the IANA zone (`Europe/Warsaw`) a side's wall clocks are in. |
Without `comparison:`, a decimal column is compared at the scale both sides share and a
floating-point one at the digits both sides hold, also when the other side is an integer
or a decimal. `numeric_scale: exact` is the opposite of
a tolerance: a decimal is compared at its declared scale and the reconciliation fails if
the two sides declare different scales, or if a side's scale is not known, rather than
rounding either one. A floating-point column under `exact` is compared digit for digit,
which only holds between two databases of the same kind, so across two different ones the
run fails with a message saying so. The audit log records the precision each numeric
column was compared at (`normalization.numeric_columns`), so a MATCH says what it was a
match at.
```yaml theme={null}
comparison: # suite default
numeric_scale: 4
reconciliations:
orders:
source:
connection: gateway
table: public.orders
target:
connection: warehouse
table: ANALYTICS.PUBLIC.ORDERS
key_columns: [order_id]
mode: row_checksum
comparison:
numeric_scale: exact
columns:
- column: fx_rate # the source-side name
numeric_scale: 8
```
`numeric_scale: ` rounds, half away from zero, so it is not a tolerance: `10.1249`
and `10.1250` differ by far less than `0.01` and still round apart at two places.
The most specific setting wins: a reconciliation's entry for the column, then its
`numeric_scale`, then the suite's entry for the column, then the suite's `numeric_scale`.
`comparison:` applies to `row_checksum` mode only; set on a `row_count` or `aggregate`
reconciliation, or on a suite with no `row_checksum` one, it is ignored with a warning.
`check-config --db` reports a column `exact` cannot be compared at before a run does.
A timestamp is compared at the precision both sides hold, the coarser of the two, and
between two different databases to the microsecond at most. That holds for a date or
timestamp key column too: the drill splits on it like any key, and a source holding
nanoseconds against a target holding microseconds puts each row in the same segment on
both sides. `timestamp_precision:` overrides it, with `exact` comparing every digit
both sides hold and failing the run when they hold different precisions.
A timestamp without a time zone is compared as its wall clock, read as UTC. When a
loader moved local wall clocks to UTC, or the other way round, name the zone each side's
wall clocks are in, and both are compared as the instant they are:
```yaml theme={null}
reconciliations:
loads:
source:
connection: gateway
table: dbo.loads
target:
connection: warehouse
table: ANALYTICS.PUBLIC.LOADS
key_columns: [load_ts, load_id]
mode: row_checksum
comparison:
timestamp_precision: millisecond
source_time_zone: Europe/Warsaw # target_time_zone is not set: UTC
```
A timestamp with a time zone already says which instant it is, so a zone set for it is
ignored with a warning. These settings are resolved like `numeric_scale`, the most
specific first, and each side's zone on its own. A zone is converted by the database,
so MySQL needs its time zone tables loaded, and Athena and Microsoft Fabric timestamps
take neither setting.
***
## 5. Reading the output
**Pick your format explicitly.** `-o` defaults to `table` for a human terminal
and to `toon` when an AI-agent environment marker is present — so as an agent
you will get `toon` unless you say otherwise. Anything parsing output must pass
`-o json`.
**`run`, `run-check` and `run-drill` render only two shapes:** `-o json`, and a
text report for every other value. `--jq`, `--columns`, `--no-headers` and
`--wide` do not apply to them; pipe their `-o json` through `jq` yourself. The
workspace commands (`suite`, `deployment`, `runs`, `audit-logs`, `connections`,
`auth`) honour all six formats and all four flags.
**`run --auto-drill -o json` is not a single JSON document.** It emits the
quick-check document, then a plain-text banner, then one drill document per
drilled reconciliation. To parse the results, run the two stages separately —
`run-check -o json` then `run-drill -o json` — each of which is one valid
document. A suite mixing modes is the exception: an `aggregate`
reconciliation's results are an array of their own, printed after any
quick-check document, so `--include` one mode at a time.
```bash theme={null}
# names that differ
synq-recon run-check suite.yaml -o json | jq -r '.results[] | select(.match == false) | .name'
# key ranges where the difference lives
synq-recon run-drill suite.yaml --include orders-daily -o json \
| jq -r '.mismatch_leaves[] | "\(.segment.min_key) .. \(.segment.max_key)"'
# ready-to-run SQL for one difference, both sides
synq-recon run-drill suite.yaml --include orders-daily -o json \
| jq -r '.investigation_queries[0] | .source_query, .target_query'
```
For a run that already finished, `audit-logs queries