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
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)
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)
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:
{
"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:
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)
GET https://coremodels.example.com/graph/integrations/snowflake/history/$PROJECT_ID
returns the rolling audit trail, grouped by vendor-side project:
{ "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.
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.
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:
{ "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 COMMENTs 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)
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:
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
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.