# Databricks — API: Every Endpoint of the CoreModels Databricks Integration, With Real Payloads

Reference articles usually show the happy path and gesture at the rest. This one is the full inventory: every HTTP route the CoreModels Databricks Unity Catalog connector answers on, the role each one enforces, the request and response bodies as they actually are, and - where a verb lives on one surface but not the other - an explicit statement of that fact rather than a diagram that implies otherwise.

# Every Endpoint of the CoreModels Databricks Integration, With Real Payloads

Reference articles usually show the happy path and gesture at the rest. This one is the full
inventory: every HTTP route the CoreModels Databricks Unity Catalog connector answers on,
the role each one enforces, the request and response bodies as they actually are, and - where
a verb lives on one surface but not the other - an explicit statement of that fact rather
than a diagram that implies otherwise.

There are two surfaces by design. The **interactive surface** under `graph/integrations/...`
authenticates with your normal CoreModels login token and carries every verb. The
**machine-to-machine surface** under `/v1/...` accepts CoreModels user API keys - the kind you
put in CI secrets - and deliberately carries only `audit` and `badge`. Base URL is written as
`https://coremodels.example.com`; the vendor key is `databricks`; `{projectId}` is the
32-character hex id of the governing project.

Any JSON route with an unrecognized vendor key answers `success: false` with
`Unknown vendor '<key>'. Registered: <comma-joined keys>.` - useful, because the registered
list doubles as discovery when you can't remember a key. (The badge routes, which must return
an image, render a gray `unknown vendor` badge instead.)

## Discovery - `GET graph/integrations/vendors` (any authenticated user)

```http
GET https://coremodels.example.com/graph/integrations/vendors
Authorization: Bearer $TOKEN
```

Lists every registered connector. The Databricks entry:

```json
{ "key": "databricks", "displayName": "Databricks Unity Catalog",
  "capabilities": "Import, Audit, Generate",
  "artifacts": {
    "information_schema": "required - JSON rows of system.information_schema COLUMNS × TABLES (documented query)",
    "keys": "optional - flattened PK/FK constraint rows (documented query)",
    "lineage": "optional - system.access.table_lineage rows; becomes Depends-On lineage"
  } }
```

All three capabilities are real for this connector, so every section below has substance -
including Generate. (Some connectors are Import + Audit only and ship no generator;
Databricks is not one of them.)

## Import - `POST graph/integrations/databricks/import/{projectId}` - Admin

The one verb that writes governed meaning, and it writes additively: existing governed nodes are never mutated or
deleted; re-imports skip what is already governed and only vendor bookkeeping is refreshed.
The body maps artifact names to raw contents - each value is the JSON text of one extract:

```http
POST https://coremodels.example.com/graph/integrations/databricks/import/{projectId}
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "artifacts": { "information_schema": "<information_schema.json contents>",
                 "keys": "<keys.json contents>",
                 "lineage": "<lineage.json contents>" },
  "spaces": [] }
```

`spaces` is optional; empty means the project's main space. Omitting `artifacts` entirely
earns the exact error `Body must include 'artifacts': { "<name>": "<content>" } (e.g.
manifest for dbt).` - the example names dbt because the message is shared across all
connectors. The response reports counts (`datasetsAdded`, `datasetsSkippedExisting`, `fieldsAdded`,
`lineageEdgesAdded`, `lineageEdgesSkipped`, `nodesEnriched`), a `snapshotStored` flag (the
parsed estate snapshot that powers re-audit), plus `lossiness` and `errors` arrays. Lossiness
is a success channel: the import proceeded, and this is what it approximated or dropped.

## Audit - `POST graph/integrations/databricks/audit/{projectId}` - Viewer

Read-only, always. `recordHistory` (default `false`) is the one piece of opt-in bookkeeping:
set it to record this run in the rolling audit trail.

```http
POST https://coremodels.example.com/graph/integrations/databricks/audit/{projectId}
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "artifacts": { "information_schema": "<information_schema.json contents>" },
  "recordHistory": true }
```

The response is the full audit report:

```jsonc
{
  "success": true,
  "vendor": "databricks", "projectName": "main",
  "errorCount": 0,          // the CI-gate signal: > 0 means governed meaning was violated
  "warningCount": 2, "infoCount": 3,
  "codes": { "key-column-undeclared": 2, "table-no-comment": 3 },
  "driftedObjects": [],     // vendor identities of drifted objects
  "fingerprint": "9f2c4a1b0e77d3aa",   // content hash of the artifacts
  "metrics": { "Datasets (estate)": "18",
               "Datasets governed": "18 / 18",
               "Fields governed": "150 / 150",
               "Governed nodes with canonical mappings": "0 / 168 (0%)",
               "Last import": "2026-08-02T14:21:07.3310000+00:00" },
  "findings": [ { "section": "Conformance", "severity": "Warning",
                  "code": "key-column-undeclared", "subject": "main.sales.orders.customer_id",
                  "message": "…", "detail": null } ],
  "markdown": "…the PR-comment-ready report…",
  "historyRecorded": true,
  "lossiness": []
}
```

Sections are `Coverage`, `Drift`, and `Conformance`; severities are `Error`, `Warning`,
`Info`. The Databricks-specific conformance codes are `table-no-comment` (Info) and
`key-column-undeclared` (Warning); the coverage and drift codes are shared across all
connectors.

`metrics` is the headline summary the Markdown report renders as a table: how many datasets
the extract contained, how many of them resolve to governed Types, the same ratio for fields,
and the canonical-mapping coverage - how many vendor-governed nodes also carry a mapping to a
non-vendor standard, which is the number to watch if you are grounding agents or exporting to
another format. `Last import` appears once an import has been recorded.

## Re-audit - `POST graph/integrations/databricks/reaudit/{projectId}` - Viewer

The audit above asks "do these fresh artifacts still conform to the governed model?" The
re-audit asks the reverse: "the governed model changed - does the last-known estate still
conform?" It needs no artifacts because it runs against the snapshot stored at import time:

```http
POST https://coremodels.example.com/graph/integrations/databricks/reaudit/{projectId}
Authorization: Bearer $TOKEN
Content-Type: application/json

{}
```

The body optionally takes `projectName` (the vendor-side project to re-audit; null means the
latest stored snapshot), `spaces`, and `notifySlack` (default `false`), which posts the run to
the project's configured Slack channel when it carries errors or warnings. The response has
the same report shape as audit, with
one behavioral difference: a re-audit run is *always* recorded in the history - there is no
`recordHistory` flag to forget. If the import ever reported `snapshotStored: false` (the
snapshot exceeded the storage cap of roughly 1.5 MB), there is nothing stored to re-audit
against, and fresh-artifact audits are the fallback.

## History - `GET graph/integrations/databricks/history/{projectId}` - Viewer

```http
GET https://coremodels.example.com/graph/integrations/databricks/history/{projectId}
Authorization: Bearer $TOKEN
```

Returns the rolling audit trail, newest first, grouped by vendor-side project:

```jsonc
{ "success": true, "vendor": "databricks",
  "projects": [ { "projectName": "main",
                  "runs": [ { "at": "2026-08-03T09:12:44.6183920+00:00", "trigger": "ci",
                              "errorCount": 0, "warningCount": 2, "infoCount": 3,
                              "codes": { "key-column-undeclared": 2, "table-no-comment": 3 },
                              "fingerprint": "9f2c4a1b0e77d3aa" } ] } ] }
```

`trigger` records how the run happened, and the values are lowercase: `audit` (an interactive
audit called with `recordHistory`), `ci` (the machine-to-machine audit), `reaudit`, and
`scheduled` (the server-side heartbeat, where a deployment enables it). Run records are
deliberately compact - counts, per-code totals, fingerprint - because the trail answers "is
this estate drifting over time?" while a live audit answers "what exactly is wrong now?"

## Badge - `GET graph/integrations/databricks/badge/{projectId}` - Viewer

Returns `image/svg+xml`: a self-contained shields-style badge labeled `databricks audit`,
rendered from the latest recorded run. Green (`#4c1`) means clean, yellow (`#dfb317`) means
warnings only, red (`#e05d44`) means errors, gray (`#9f9f9f`) means no recorded runs.

## Generate - `POST graph/integrations/databricks/generate/{projectId}` - Viewer

Read-only in the graph sense: it reads governed meaning and emits vendor artifacts for you to
review and apply.

```http
POST https://coremodels.example.com/graph/integrations/databricks/generate/{projectId}
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "typeNames": [] }
```

`typeNames` restricts generation to named types; empty means everything eligible. (The body
also accepts `targetVersion` and `extra` for connectors with dialect choices; the Databricks
generator currently has a single dialect, so `typeNames` is the option that matters.) The
response:

```jsonc
{ "success": true,
  "artifacts": [ { "name": "coremodels_delta_tables.sql", "kind": "sql",
                   "content": "-- Generated by CoreModels - governed Delta table definitions.\n…" } ],
  "lossiness": [ { "kind": "StructuralDrop", "path": "orders_view",
                   "explanation": "Views are derived objects; DDL generation covers tables only." } ],
  "errors": [] }
```

One artifact comes back: `CREATE TABLE IF NOT EXISTS … USING DELTA` statements with
`NOT NULL` from governed checks, one informational `PRIMARY KEY` constraint per table,
`FOREIGN KEY … REFERENCES` from governed references, and taxonomy allowed values carried on
column COMMENTs. Views are skipped with declared lossiness, as the sample shows. If nothing
is eligible, generation fails honestly with `No eligible tables found to generate DDL for.`

## Status - `GET graph/integrations/databricks/status/{projectId}` - Viewer

```http
GET https://coremodels.example.com/graph/integrations/databricks/status/{projectId}
Authorization: Bearer $TOKEN
```

Answers "has this project imported a Databricks estate, and what does it look like now?":
`imported` (boolean), `state` (the last-import bookkeeping), and `governedDatasets` - how many
Unity Catalog identities currently resolve to governed Types.

The `state` record for this connector carries the vendor key, the vendor-side project name
(your catalog), `importedAt` as an ISO-8601 timestamp, a compact `counts` string
(`fieldsAdded=…, lineageAdded=…, lineageSkipped=…, nodesEnriched=…`), the artifact
`fingerprint`, and the parser's `facts` - for Databricks, the table and view totals. The
tool-version and artifact-version slots stay empty here by nature: a Unity Catalog SQL extract
carries no generating tool version, unlike, say, a build manifest that stamps one.

## Reconcile - `POST graph/integrations/reconcile/{projectId}` - Admin

Not vendor-scoped in the route, but part of the surface you will reach for once a lakehouse is
governed from two sides - the Unity Catalog metadata on one, whatever transformation tool
materializes into it on the other. It finds datasets that two imported estates describe as the
same physical relation, matched on `database.schema.table`, and links each pair with reciprocal
sameAs mappings:

```http
POST https://coremodels.example.com/graph/integrations/reconcile/{projectId}
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "vendorA": "databricks", "vendorB": "dbt" }
```

The response reports `linksWritten`, the matched `pairs` (both vendor ids, the physical name,
the count of matched fields), and `unmatchedA` / `unmatchedB` so you can see what did not line
up. It is idempotent - re-running after the next import refreshes the links rather than
duplicating them.

## Sync plans - `POST graph/integrations/databricks/sync/propose/{projectId}` - Viewer

The newest part of the surface. Propose takes the same `artifacts` body as audit and
classifies the fresh extract three-way - against the estate snapshot stored at import and the
per-node governed sync bases - into a reviewable, replayable plan, returned inline. It is
read-only against governed meaning; its only writes are bookkeeping (the stored plan plus a
ledger entry, superseding earlier proposals). Two Viewer reads complete it:
`GET graph/integrations/sync/plan/{projectId}/{planId}` fetches a stored plan, and
`GET graph/integrations/sync/ledger/{projectId}` (optionally `?vendor=databricks`) lists the
compact plan history per vendor-side project, newest first.

## The machine-to-machine surface (`/v1`)

Two routes, both Viewer role, both taking user API keys - everything CI needs and nothing it
doesn't:

```http
POST https://coremodels.example.com/v1/{projectId}/integrations/databricks/audit
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "artifacts": { "information_schema": "<information_schema.json contents>" },
  "recordHistory": true }
```

Same request body as the interactive audit; the response is wrapped in the standard
`ApiResponse` envelope, so the report fields live under `data` - the CI gate reads
`data.errorCount`, and `data.markdown` is the report for your job summary. Runs recorded from
this surface carry trigger `ci`. The second route is the same badge as above:

```http
GET https://coremodels.example.com/v1/{projectId}/integrations/databricks/badge
Authorization: Bearer $TOKEN
```

And the honest boundary: `reaudit`, `history`, `import`, `generate`, `status`, `reconcile`,
and the sync-plan routes live on the interactive surface only. If your automation needs re-audit on a schedule, that is what the
server-side scheduled re-audit (a deployment configuration, off by default) is for - it runs
against stored snapshots and records into the same history the routes above read.

## Role summary

| Route | Role |
|---|---|
| `GET graph/integrations/vendors` | any authenticated |
| `POST …/databricks/import/{projectId}` | Admin |
| `POST …/databricks/audit/{projectId}` | Viewer |
| `POST …/databricks/reaudit/{projectId}` | Viewer |
| `GET …/databricks/history/{projectId}` | Viewer |
| `GET …/databricks/badge/{projectId}` | Viewer |
| `POST …/databricks/generate/{projectId}` | Viewer |
| `GET …/databricks/status/{projectId}` | Viewer |
| `POST graph/integrations/reconcile/{projectId}` | Admin |
| `POST …/databricks/sync/propose/{projectId}` | Viewer |
| `GET graph/integrations/sync/plan/{projectId}/{planId}` | Viewer |
| `GET graph/integrations/sync/ledger/{projectId}` | Viewer |
| `POST v1/{projectId}/integrations/databricks/audit` | Viewer (API key) |
| `GET v1/{projectId}/integrations/databricks/badge` | Viewer (API key) |

Import and reconcile are the only verbs that need Admin, because they are the only two that
write governed meaning to the graph. Everything else - including generate, which writes files
for you rather than nodes for the graph - runs at Viewer.

For worked end-to-end examples of these calls, see the Databricks quickstart in the
CoreModels documentation.
