# Snowflake — API: Every Endpoint: The CoreModels HTTP Surface for Snowflake

This is the reference walk through everything you can do with the Snowflake integration over plain HTTP - every route, its role requirement, its request body, and what comes back. The Snowflake connector declares all three capabilities - Import, Audit, and Generate - so every verb below is live for it.

# Every Endpoint: The CoreModels HTTP Surface for Snowflake

This is the reference walk through everything you can do with the Snowflake integration over plain HTTP - every route, its role requirement, its request body, and what comes back. The Snowflake connector declares all three capabilities - Import, Audit, and Generate - so every verb below is live for it.

Two surfaces exist, on purpose. The **interactive surface** under `graph/integrations/...` authenticates with your normal CoreModels login token and carries the full verb set. The **machine-to-machine surface** under `/v1` accepts user API keys (JWTs) and deliberately carries only the two verbs automation needs: `audit` and `badge`. We'll cover both, in that order. Throughout, `https://coremodels.example.com` stands in for your deployment, `$TOKEN` for your credential, and `$PROJECT_ID` for the 32-character hex id of the governing project.

## Discovery: what's registered

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

Any authenticated user can call this. It returns each registered connector with `key`, `displayName`, a `capabilities` string, and an `artifacts` map describing what each artifact is and whether it's required. For Snowflake the artifact notes read: `information_schema` is required (JSON rows of `INFORMATION_SCHEMA.COLUMNS × TABLES`), `keys` is optional (`SHOW PRIMARY KEYS` + `SHOW IMPORTED KEYS` rows, JSON-ified via `RESULT_SCAN`), and `object_dependencies` is optional (`ACCOUNT_USAGE.OBJECT_DEPENDENCIES` rows, which become lineage edges).

If you ever pass a vendor key that isn't registered, every route answers the same way: `success: false` with the message `Unknown vendor '<v>'. Registered: <comma-joined keys>.` - so a typo is diagnosed in one round trip.

## The verb set at a glance

| Route | Method | Role | Writes? |
|---|---|---|---|
| `graph/integrations/snowflake/import/{projectId}` | POST | Admin | additive graph writes |
| `graph/integrations/snowflake/audit/{projectId}` | POST | Viewer | no (history opt-in) |
| `graph/integrations/snowflake/reaudit/{projectId}` | POST | Viewer | history record only |
| `graph/integrations/snowflake/history/{projectId}` | GET | Viewer | no |
| `graph/integrations/snowflake/badge/{projectId}` | GET | Viewer | no |
| `graph/integrations/snowflake/generate/{projectId}` | POST | Viewer | no |
| `graph/integrations/snowflake/status/{projectId}` | GET | Viewer | no |
| `graph/integrations/reconcile/{projectId}` | POST | Admin | sameAs links |
| `graph/integrations/snowflake/sync/propose/{projectId}` | POST | Viewer | plan blob + ledger entry |
| `graph/integrations/sync/plan/{projectId}/{planId}` | GET | Viewer | no |
| `graph/integrations/sync/ledger/{projectId}` | GET | Viewer | no |
| `v1/{projectId}/integrations/snowflake/audit` | POST | Viewer | no (history opt-in) |
| `v1/{projectId}/integrations/snowflake/badge` | GET | Viewer | no |

The posture behind the table: import writes only to the graph, and only additively; audit and generate never write anything. Recording an audit run in the history is opt-in bookkeeping, and only the reaudit verb always records its run. Sync propose is read-only against governed meaning too - its only writes are bookkeeping (the stored plan blob and a ledger entry).

## Import (Admin)

```http
POST https://coremodels.example.com/graph/integrations/snowflake/import/$PROJECT_ID
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "artifacts": { "information_schema": "<information_schema.json content>",
                 "keys": "<keys.json content>",
                 "object_dependencies": "<object_dependencies.json content>" } }
```

The body shape is `ArtifactsRequest`: an `artifacts` map of name to raw content (required - an empty map is rejected with a message telling you so), plus an optional `spaces` array of space ids (empty means the project's main space). The response reports `datasetsAdded`, `datasetsSkippedExisting`, `fieldsAdded`, `lineageEdgesAdded`, `lineageEdgesSkipped`, `nodesEnriched`, `snapshotStored`, and the `lossiness`/`errors` lists. Re-imports never mutate existing governed nodes - changed objects surface through the audit instead.

## Audit (Viewer)

```http
POST https://coremodels.example.com/graph/integrations/snowflake/audit/$PROJECT_ID
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "artifacts": { "information_schema": "<fresh extract>" }, "recordHistory": true }
```

Same body shape as import, plus `recordHistory` (default `false` - the audit verb stays strictly read-only unless asked). The response is the full audit report:

```jsonc
{
  "success": true,
  "vendor": "snowflake", "projectName": "ANALYTICS",
  "errorCount": 0,            // CI gate: > 0 ⇒ fail the build
  "warningCount": 2, "infoCount": 3,
  "codes": { "semi-structured-column": 1, "key-column-undeclared": 1, "table-no-comment": 3 },
  "driftedObjects": [],
  "fingerprint": "9f2c41aa08d13be7",
  "metrics": { },
  "findings": [ { "section": "...", "severity": "...", "code": "...",
                  "subject": "...", "message": "...", "detail": "..." } ],
  "markdown": "...",          // PR-comment-ready report
  "historyRecorded": true,
  "lossiness": []
}
```

## Reaudit (Viewer)

The audit above asks "do these fresh artifacts still conform to the governed model?" Reaudit asks the opposite question - "does the governed model still match the last-known estate?" - by running the same engine over the snapshot stored at import time against the *current* governed model. No artifacts needed:

```http
POST https://coremodels.example.com/graph/integrations/snowflake/reaudit/$PROJECT_ID
Authorization: Bearer $TOKEN
Content-Type: application/json

{}
```

The optional body fields are `projectName` (the vendor-side project to re-audit; null means the latest stored snapshot) and `spaces`. Reaudit always records its run in the history - that's the one verb where recording isn't opt-in, because a re-audit exists precisely to extend the trail. If the import ever reported `snapshotStored: false` (very large estates exceed the snapshot storage cap of roughly 1.5 MB), there is no stored snapshot to run against and reaudit tells you so; fresh-artifact audits keep working regardless.

## History and badge (Viewer)

```http
GET https://coremodels.example.com/graph/integrations/snowflake/history/$PROJECT_ID
```

returns the rolling audit trail, grouped by vendor-side project:

```json
{ "success": true, "vendor": "snowflake",
  "projects": [ { "projectName": "ANALYTICS",
                  "runs": [ { "at": "2026-07-27T09:14:03Z", "trigger": "reaudit",
                              "errorCount": 0, "warningCount": 2, "infoCount": 3,
                              "codes": { "table-no-comment": 3 },
                              "fingerprint": "9f2c41aa08d13be7" } ] } ] }
```

Runs carry their trigger (`audit`, `reaudit`, `ci`, or `scheduled` for the optional server-side heartbeat), so the trail shows not only what drifted but which loop caught it. The badge route renders the latest recorded run as a self-contained SVG (`image/svg+xml`): green for clean, yellow for warnings only, red for errors, gray when nothing has been recorded yet.

```http
GET https://coremodels.example.com/graph/integrations/snowflake/badge/$PROJECT_ID
```

## Generate (Viewer)

Snowflake's generator closes the loop outward: governed entities become one SQL artifact of `CREATE OR REPLACE TABLE` statements.

```http
POST https://coremodels.example.com/graph/integrations/snowflake/generate/$PROJECT_ID
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "typeNames": [] }
```

`typeNames` restricts generation to the named types (empty means everything eligible); `extra` is an open string-to-string bag for vendor-specific options; `targetVersion` exists on the request for vendors with dialect switches (dbt uses it) - the Snowflake generator doesn't take one. The response carries the artifacts:

```json
{ "success": true,
  "artifacts": [ { "name": "coremodels_tables.sql", "kind": "sql", "content": "..." } ],
  "lossiness": [ { "kind": "StructuralDrop", "path": "CUSTOMER_SUMMARY_V",
                   "explanation": "Views are derived objects; DDL generation covers tables only." } ],
  "errors": [] }
```

Inside the script: column types come from the vendor-native type recorded at import when present, otherwise from IR defaults (`NUMBER(38,0)`, `FLOAT`, `BOOLEAN`, `TIMESTAMP_NTZ`, `TEXT`); `NOT NULL` comes from recorded checks; `PRIMARY KEY` and `FOREIGN KEY` declarations come from unique-key checks and governed references (informational in Snowflake - not enforced, but read by tools); and table/column `COMMENT`s carry descriptions plus, for taxonomy-constrained columns, the governed allowed values - because Snowflake has no enforced CHECK constraints, the comment is where that meaning can ride. Views are skipped with declared lossiness, as the example shows.

## Status (Viewer)

```http
GET https://coremodels.example.com/graph/integrations/snowflake/status/$PROJECT_ID
```

Returns `{ success, vendor, imported, state, governedDatasets }` - whether an import has happened, the last-import state record (timestamps, artifact fingerprint, counts), and how many governed datasets currently trace back to Snowflake identities.

## Reconcile (Admin)

If the same physical tables are governed from two sides - say a dbt project and the Snowflake warehouse it materializes into - reconciliation links each matched pair as one entity via reciprocal sameAs assertions:

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

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

The response lists `linksWritten`, the matched `pairs` (with `vendorIdA`, `vendorIdB`, `physicalName`, `matchedFields`), and the unmatched leftovers on each side. The operation is idempotent - run it again after the next import and existing links are refreshed rather than duplicated.

## Sync plans (Viewer)

Beyond the audit loop, three routes carry the sync-plan flow. `POST graph/integrations/snowflake/sync/propose/{projectId}` takes the same `ArtifactsRequest` body as audit and classifies fresh artifacts three-way - against the estate snapshot stored at import and the current governed model - into a reviewable, replayable plan, returned inline. Its only writes are bookkeeping: the stored plan blob and a Proposed ledger entry that supersedes earlier proposals. `GET graph/integrations/sync/plan/{projectId}/{planId}` fetches a stored plan for review, and `GET graph/integrations/sync/ledger/{projectId}?vendor=snowflake` returns the compact plan history per vendor-project, newest first.

## The v1 surface: audit and badge for machines

```http
POST https://coremodels.example.com/v1/$PROJECT_ID/integrations/snowflake/audit
Authorization: Bearer $TOKEN
Content-Type: application/json

{ "artifacts": { "information_schema": "<fresh extract>" }, "recordHistory": true }
```

Same request body, same report - but wrapped in the standard `ApiResponse` envelope, so all the counts live under `data.*` (`data.errorCount`, `data.markdown`, and so on), and when `recordHistory` is true the run is recorded with trigger `ci`. The companion `GET v1/$PROJECT_ID/integrations/snowflake/badge` serves the same SVG badge on an API key. Note what the v1 surface intentionally does not carry: `reaudit` and `history` stay on the interactive surface - CI needs a gate and a badge, not the whole console.

That's the entire surface: one discovery route, seven per-vendor audit-loop verbs, the three sync-plan routes, one cross-vendor reconcile, and a two-verb machine surface. The Snowflake quickstart in our docs pairs each of these calls with the exact Snowsight extraction recipes that produce the artifacts.
