Import from dbt
Administrators with the Semantic models permission.
If your team maintains a dbt project, you don't have to describe your data twice. Axiome imports the artifacts dbt already produces and turns them into semantic models — models, keys, dimensions, measures, and metrics — so your dbt project stays the single source of truth.
There are three ways to get definitions in, from quickest to most automated:
- Manual upload — pick a file, preview, confirm. Good for a first look.
- CI ingest tokens — a
curlstep in any CI pushes the manifest after every build. - GitHub sync — install the Axiome GitHub App once and every merge imports automatically. No tokens, no upload step.
All three feed the same pipeline: the same preview rules, the same import report, the same safety gate.
What gets imported
dbt writes its artifacts to target/ on every parsing command (dbt parse, dbt run, dbt build). Axiome reads two of them:
| File | What Axiome imports |
|---|---|
target/semantic_manifest.json | MetricFlow definitions — semantic models, entities (as keys), dimensions, measures, and metrics. The richest import; use it if your project defines MetricFlow semantics. |
target/manifest.json | Metadata only — model descriptions and relationships tests (as keys and joins). The fallback for projects without MetricFlow definitions. |
One import takes one file — the two are alternative descriptions of the same project, not complements.
Projects still using the legacy top-level semantic_models: YAML produce an empty semantic_manifest.json on current dbt versions (warning dbt1157). Migrate with dbt-autofix deprecations --semantic-layer, re-run dbt parse, and import the new artifact.
Where imported definitions live
Imported definitions become the governed layer of the connection: they sit on top of the baseline Axiome generates from your schema, and under the edits administrators make in the workbench. Two consequences worth knowing:
- Your workbench edits win. Renaming a dimension or refining a description in Axiome is never overwritten by a re-import.
- Every import replaces the whole governed layer. The layer always mirrors exactly one manifest — there is no partial merge, so Axiome and dbt can never drift apart.
While an import is active, the workbench shows a banner — "12 models are governed by a dbt import — last imported …" — and each governed element carries a dbt provenance badge.
Import manually
- Go to Settings → Admin → Semantic models and pick the connection in the selector at the top.
- Click Import from dbt.
- Choose file (or paste the JSON directly). Axiome tells you which kind of manifest it detected.
- Click Preview import and read the report — nothing is saved yet.
- Click Confirm import.
Reading the preview report
The report counts what will be imported per kind (Models, Keys, Dimensions, Measures, Metrics) and flags everything that needs your attention:
- Imported with reduced fidelity — the element comes in, minus a capability. Example: "Validity windows (SCD) are not modeled — imported as a plain dimension."
- Skipped — the element is left out, with the reason. Example: "The table behind this model was not found in the connected schema."
- Metrics that would not compile — every metric is test-compiled against your live database schema before import, so a definition referencing a column that doesn't exist is caught here, not on a dashboard.
- Changes vs the current import — on a re-import: which models are Added, Removed, or Changed.
If the manifest is identical to what's already imported, the preview says so and there is nothing to confirm.
Automate it from CI
Once the manual import looks right, wire it into CI so Axiome follows your dbt project by itself. Pick one:
| GitHub sync | CI ingest tokens | |
|---|---|---|
| Works with | GitHub + GitHub Actions | Any CI that can run curl |
| Setup | Install the app, track the repo | Create a token, add a push step |
| Secrets to manage | None | One bearer token |
| Feedback | Delivery status in Axiome | HTTP status in the CI job itself |
Both are gated the same way: clean manifests apply on their own, risky ones are held for review.
GitHub sync
First, make your workflow upload the artifacts after dbt parse / dbt build:
- name: Upload dbt artifacts for Axiome
uses: actions/upload-artifact@v4
with:
name: axiome-dbt-artifacts
path: |
target/semantic_manifest.json
target/manifest.json
Then, in the workbench:
- Open the ⋯ menu next to Import from dbt and choose GitHub sync.
- Click Connect GitHub and install the Axiome app on the organization that hosts your dbt repository. (If your organization requires owner approval, come back and reconnect once it's granted.)
- Pick the Repository, the Branch to track (defaults to the repository's default branch), and the Artifact name — it must match the
name:in your upload step;axiome-dbt-artifactsis the default on both sides. - Click Track repository.
From then on, every successful workflow run on the tracked branch triggers an import. Each delivery imports one file: semantic_manifest.json when it has semantic models, otherwise manifest.json — which is why the upload step includes both. Re-deliveries of the same manifest are recognized and do nothing.
The dialog shows the last delivery's outcome:
| Status | Meaning |
|---|---|
| applied | Imported and live |
| held for review | Waiting for an admin — see the review gate |
| already up to date | Same manifest as the current import |
| nothing importable | The artifact contained no usable definitions |
| no artifact found | No artifact with the configured name on that run — check the name: in your upload step |
| failed | The delivery errored — retry by re-running the workflow |
Untrack stops imports from a repository (already-imported models stay). Disconnect removes the integration for the whole workspace — note it does not uninstall the app on GitHub; remove it from your GitHub organization settings as well.
CI ingest tokens
For GitLab, Bitbucket, Jenkins, or any other CI:
- Open the ⋯ menu and choose CI ingest tokens.
- Give the token a label (e.g.
gitlab-ci-main) and click Create token. - Copy the token now — it is shown only once — and store it as a secret in your CI.
- Add the push step shown in the dialog to your pipeline, after
dbt parse:
curl --fail-with-body -X POST "https://app.axiome.cc/api/semantic-import/dbt" \
-H "Authorization: Bearer $AXIOME_DBT_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Axiome-Ref: $GITHUB_REF_NAME" -H "X-Axiome-Sha: $GITHUB_SHA" -H "X-Axiome-Run-Id: $GITHUB_RUN_ID" \
--data-binary @target/semantic_manifest.json
The body is the raw manifest — no wrapper, no form encoding. The three X-Axiome-* headers are optional git coordinates; sending them makes held pushes and history entries traceable to a commit (adapt the variables to your CI's equivalents).
The response tells your pipeline what happened:
| HTTP status | Meaning |
|---|---|
| 200 | Applied — or already up to date |
| 202 | Held for review — an admin needs to approve it in Axiome |
| 422 | Nothing importable in the manifest |
| 401 | The token is invalid or revoked |
With --fail-with-body, the step fails the job on 401/422 so a misconfiguration can't pass silently; a 202 still succeeds, since a held push is a normal outcome.
Each connection can have up to 10 active tokens. Revoke stops a token immediately; the list shows when each was last used.
Clean imports apply, risky imports are held
CI imports run without a human in the loop, so Axiome applies a safety gate. A pushed manifest is applied automatically unless:
- a metric would not compile against the live schema, or
- a currently governed metric would disappear — the strongest signal of a wrong artifact (stale branch, truncated file, wrong project).
In those cases the push is held: nothing changes in your semantic layer, and the workbench shows a banner — "A dbt manifest pushed on … is held for review." Click Review to see exactly which governed metrics would disappear and which metrics fail to compile, plus the commit it came from. Then either:
- Approve & apply — Axiome re-runs the full import against the live schema and applies it, or
- Discard — the push is dropped; the next CI push starts fresh.
There is only ever one held push per connection — a newer push replaces it, so after a fix lands in the repo there's nothing to clean up.
Manual uploads are never held: the preview → confirm flow is the review.
Import history
The ⋯ → Import history dialog lists every import for the connection, newest first: date, source (Upload, CI push, or GitHub — with branch and commit when sent), model and metric counts, and status (Applied, Held, Discarded, or Superseded), including who approved a held push.
Removing the import
⋯ → Remove import deletes the governed layer: the affected models fall back to the generated baseline, and edits made in the workbench are kept. You can re-import at any time — including approving a currently held push onto the emptied connection.