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
Type carries type_id (1–1000), name (max 100 chars), svg_icon (max 1 MB) and
traits.
Entities
synq.entities.custom.v1.EntitiesService
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
A
Feature is entity_id + feature_id (max 255 chars, stable) + one of:
ListEntityFeatures with entity_id unset returns the features of every custom entity in
the workspace.
Groups
synq.entities.custom.v1.GroupsService
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
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
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
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.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 byCustomIdentifier:
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-<slug>.
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/<entity_id> 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, the entity_url in a webhook payload,
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
TheList* 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
EveryUpsert* 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.