Direct Connections

Connections to data warehouses, Cube, and dbt semantic layers.

A direct connection stores credentials/config for a data source. Secrets (passwords, keys) are never returned — see What connectionFields contains for what you do get back, which depends on who owns the connection.

Using one to create a metric? See Importing a dbt metric or Creating a metric from a data warehouse or Cube connection.

Full field reference (types, enums, required/read-only flags) is generated from components.schemas.DirectConnection in the API Reference tab, and tabulated field by field — with what each one's read-only status means for a write — in Field Reference. Or fetch the live OpenAPI document directly, see Getting Started for the URL.

Two kinds of connection

The CRUD endpoints cover both kinds, and so does get_connection_fields, but the per-connection metadata sub-endpoints do not — each kind exposes what it can query in a different way:

KindIdentified byMetadata endpoint
Direct databasedataBaseType is any other value/tables and /tables/:tableName/fields
dbtdataBaseType is "dbt"/metrics

Asking the wrong one for a connection is a 400 naming the endpoint that does serve it, so the two cases stay distinguishable from a connection that does not exist (404).

GET /direct_connections · GET /direct_connections/:connectionId

{
  "id": "13451dc615244a9cb6c99203a3abbea5",
  "name": "Prod Postgres",
  "dataBaseType": "postgresql",
  "ownerId": "7c2f599e6dfb402996b2194ab9b11164",
  "isShared": true,
  "connectionFields": { "user": "reporting_ro", "_host": "db.example.com", "_port": 5432, "_database": "analytics", "currentSchema": "public" },
  "version": "1.0"
}

Field names under connectionFields vary by dataBaseType — each connector defines its own parameter names (e.g. Postgres uses _host/_port/_database; other connectors differ).

What the list returns

GET /direct_connections returns the connections available to you: the ones you own, plus the ones shared across the account. isShared is what distinguishes them — it marks a connection as shared with every user in the account, rather than with any particular user. Another user's unshared connection is not yours to see, and the detail endpoint answers 404 for one.

What connectionFields contains

connectionFields is populated only for connections you own. A connection shared with the account comes back with an empty object, even though you can otherwise read and query it:

{
  "id": "8944c31f217bed24f295a26c5d39b9d0",
  "name": "Klipfolio Analytics",
  "dataBaseType": "bigquery",
  "ownerId": "71f30bd60e4fb72bf8703743ae1084ec",
  "isShared": true,
  "connectionFields": {},
  "version": "1.0"
}

The one exception is the built-in sample connection (dataBaseType: "sample"), which reports its fields to everyone — it holds no private credentials.

Within a connection you own, secret fields (passwords, private keys, OAuth credentials) are omitted rather than masked — so a field you set on create may be absent when you read it back. Everything non-secret is returned as a plain scalar, as in the example above.

A missing secret is not a problem for a subsequent update, though: an update keeps every secret whose key it does not send, so you can GET a connection and PUT it back unchanged without its credentials being lost. See Updating without resending secrets.

GET /direct_connections/get_connection_fields/:dataBaseType

What to send under connectionFields for a given connector. Read-only, and about the connector type rather than any connection — no connection need exist, and dataBaseType is matched case-insensitively (Snowflake works).

The response is an array of arrays. Each inner array is one complete set of fields that satisfies the connection — send the fields of one set, not a mixture of two.

FieldTypeNotes
namestringthe key to use under connectionFields
typestringthe kind of value expected, as the connector reports it — e.g. string, password, port, host, keyFile, certFile, or choice
requiredbooleanwhether the field must be present in this set
sensitivebooleanwhether the value is a secret — see below
valuestringchoice fields only — the value this set fixes the field to; send it verbatim

There is more than one set whenever the connector offers a choice. Snowflake, for example, authenticates either by password or by key pair, so it publishes two:

[
  [
    { "name": "user", "type": "string", "required": true, "sensitive": false },
    { "name": "_authType", "type": "choice", "required": false, "sensitive": false, "value": "USER_PASSWORD" },
    { "name": "password", "type": "password", "required": true, "sensitive": true },
    { "name": "_accountId", "type": "string", "required": true, "sensitive": false },
    { "name": "db", "type": "string", "required": true, "sensitive": false },
    { "name": "schema", "type": "string", "required": true, "sensitive": false },
    { "name": "warehouse", "type": "string", "required": true, "sensitive": false },
    { "name": "role", "type": "string", "required": false, "sensitive": false }
  ],
  [
    { "name": "user", "type": "string", "required": true, "sensitive": false },
    { "name": "_authType", "type": "choice", "required": false, "sensitive": false, "value": "KEY_PAIR" },
    { "name": "privateKey__privateKey", "type": "keyFile", "required": true, "sensitive": true },
    { "name": "_privateKey_passphrase", "type": "password", "required": false, "sensitive": true },
    { "name": "_accountId", "type": "string", "required": true, "sensitive": false },
    { "name": "db", "type": "string", "required": true, "sensitive": false },
    { "name": "schema", "type": "string", "required": true, "sensitive": false },
    { "name": "warehouse", "type": "string", "required": true, "sensitive": false },
    { "name": "role", "type": "string", "required": false, "sensitive": false }
  ]
]

A choice field is a field like any other: pick a set, then send the choice field with the value that set names, alongside the rest of the set.

A connector with no choices publishes a single set — that array of one is still the response shape:

[
  [
    { "name": "environmentId", "type": "string", "required": true, "sensitive": false },
    { "name": "token", "type": "password", "required": true, "sensitive": true },
    { "name": "hostName", "type": "host", "required": true, "sensitive": false }
  ]
]

Sensitive fields, create versus update

sensitive: true marks a secret — a password, token, private key or certificate. Secrets are accepted on POST but never returned by any read (see What connectionFields contains), so on create you must supply every one the set requires.

On PUT you do not: see Updating without resending secrets.

An unknown dataBaseType responds 404, naming the types that do exist.

POST /direct_connections

Two fields are required on every write:

  • dataBaseType — the connector type (e.g. postgresql, snowflake, redshift, bigquery, databricks, mariadb, aurora, azuresql, azuresynapse, dbt, cube, sample).
  • isShared — whether the connection is shared with every user in the account. There is no default; send false for a connection only you can use.

A body missing either is rejected with 400. Returns 201 with the meta/data envelope.

{
  "name": "Prod Postgres",
  "dataBaseType": "postgresql",
  "isShared": true,
  "connectionFields": {
    "user": "reporting_ro",
    "password": "<secret>",
    "_host": "db.example.com",
    "_port": 5432,
    "_database": "analytics",
    "currentSchema": "public"
  }
}

Field names under connectionFields vary by dataBaseType, as above. Rather than guessing them, ask GET /direct_connections/get_connection_fields/:dataBaseType for the sets of fields that connector accepts.

Pass "dbt" to create a dbt semantic-layer connection; it is stored in a different place from the database connectors, which is why it takes no connectionFields of the kind they do.

PUT /direct_connections/:connectionId

Replace editable fields; returns the updated connection. dataBaseType and isShared are required here too, so resend both.

Updating without resending secrets

Secrets are the exception to "replace editable fields": an update preserves any sensitive field whose key it does not send. Since a read omits those keys entirely (see What connectionFields contains), the spec you get back from GET is exactly what PUT accepts — you can send it straight back and the stored credentials survive.

So on an update, a sensitive field has three behaviours:

What you sendWhat happens
the key is absentthe stored secret is kept
a new valuethe stored secret is replaced
an empty value ("" or null)the secret is cleared

Which fields count as sensitive is per connector — ask get_connection_fields and read the sensitive flag. Non-sensitive fields follow the ordinary replace rule, so an editable one you omit is cleared as usual.

DELETE /direct_connections/:connectionId

204 No Content.

GET /direct_connections/:connectionId/tables

List the tables the connection can query. Read-only.

FieldTypeNotes
namestringtable identifier, as used in the fields path
descriptionstring
typeenumCUBE | CUBE_VIEW — always upper-cased by the gateway
[
  { "name": "orders", "description": "All orders", "type": "CUBE" },
  { "name": "order_summary", "description": "Orders rolled up by month", "type": "CUBE_VIEW" }
]

Table metadata is carried only by direct database connections — those whose dataBaseType is anything other than dbt:

SituationResponse
The connection is a dbt connection400 — use /metrics instead
No connection with that id404
Direct connection exposing no table metadata404
Direct connection with no tables right now200 with []

GET /direct_connections/:connectionId/tables/:tableName/fields

List the fields of one of the connection's tables. Read-only. A tableName containing characters that are significant in a URL path must be percent-encoded.

FieldTypeNotes
namestringfield identifier, as used in a query
labelstringshort display name; falls back to name
descriptionstring
dataTypestringthe column's type as the connection reports it, passed through unchanged — e.g. string, double, int, datetime, boolean
typestringpassed through unchanged — e.g. metric, dimension

dataType and type are not case-normalized (unlike type on a table), for consistency with GET /query_builder/services/:serviceName/views/:viewName/fields, which describes columns the same way.

[
  { "name": "orders.amount", "label": "Amount", "description": "Order total", "dataType": "double", "type": "metric" },
  { "name": "orders.created_at", "label": "Created At", "dataType": "datetime", "type": "dimension" }
]

An unknown tableName responds 404, and a dbt connection responds 400, as above.

GET /direct_connections/:connectionId/metrics

List the metrics a dbt connection's project defines. Read-only.

These are candidate metrics, not metrics that exist yet: each one references a metric defined in dbt and carries no id. They are returned in the same public shape the metrics endpoints use, so an entry can be sent to POST /metrics to create it for the current user. Creating one is not required — the list describes everything available in the dbt project.

sourceType is DBT, sourceId is the connection's id, and fieldId is the metric's name in dbt. Those three are what identify the dbt metric on create.

An entry can be posted back unchanged: POST /metrics ignores the read-only properties.sl*/supportedPeriodicities entries rather than rejecting them, so there is nothing to strip first. They describe the upstream dbt definition, so there is nothing for a create to apply. (The internal slMetricDigest is not returned here at all — see the Metrics guide.)

[
  {
    "name": "Total Revenue",
    "description": "Sum of order revenue",
    "sourceType": "DBT",
    "sourceId": "conn_dbt_1",
    "fieldId": "total_revenue",
    "timeRefId": "metric_time",
    "properties": {
      "numberFormat": "NUMERIC",
      "dataShape": "TRANSACTIONAL",
      "snapshotPeriod": "none",
      "timelessData": true,
      "slMetricType": "SIMPLE",
      "supportedPeriodicities": "1d,1w,1mo"
    },
    "dimensions": [
      { "id": "sl_abc123", "name": "Region", "fieldId": "customer__region" }
    ],
    "version": "1.0"
  }
]
SituationResponse
The connection is a direct database connection400 — use /tables instead
No connection with that id404
dbt connection whose project defines no metrics200 with []

Did this page help you?