> ## Documentation Index
> Fetch the complete documentation index at: https://platform.atlan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish a logical model

> Define logical business concepts, publish an immutable ontology release, and retrieve bounded context for agents.

The Ontology API stores the logical meaning of business concepts. It does not
copy customer records into Agent Registry, map warehouse tables, generate SQL,
or execute actions.

This guide explains the complete ontology lifecycle. It uses a small example
model to show how logical resources become one immutable release:

```text theme={null}
ontology → logical drafts → validation → release → bounded agent context
```

<Note>
  **Preview.** An administrator must install the Ontology extension in the
  account before these routes and MCP tools are available. Use a disposable
  workspace while evaluating the contract.
</Note>

## Before you start

Set the gateway URL, a bearer token from an approved secret store, and a
workspace available to that identity:

```bash theme={null}
export ATLAN_GATEWAY_URL="https://agentgateway.atlan.engineering"
export ATLAN_TOKEN="..."
export WORKSPACE_ID="workspace_01example"
```

The examples also use `jq` to retain artifact IDs, exact version ordinals, and
content hashes from create responses. Those three values identify the precise
draft version that publication consumes.

## Create the ontology namespace

An ontology is the governance root for its logical resources. The
`backward_compatible` policy prevents a minor or patch release from removing a
published contract.

```bash theme={null}
ONTOLOGY=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/registry/v1/artifacts/ontology" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg workspace_id "${WORKSPACE_ID}" \
    '{
      name: "example-ontology",
      workspace_id: $workspace_id,
      api_name: "example_ontology",
      compatibility_policy: "backward_compatible"
    }')")

ONTOLOGY_ID=$(jq -r '.id' <<<"${ONTOLOGY}")
```

## Define the logical resources

Create a `Customer` object, a `SupportCase` object, and a typed link between
them. Property value schemas stay inside their owning object because they share
that object's lifecycle.

```bash theme={null}
CUSTOMER=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/registry/v1/artifacts/ontology_object_type" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg workspace_id "${WORKSPACE_ID}" \
    --arg ontology_id "${ONTOLOGY_ID}" \
    '{
      name: "customer",
      workspace_id: $workspace_id,
      ontology_id: $ontology_id,
      api_name: "customer",
      object_kind: "entity",
      implements: [],
      properties: [
        {
          api_name: "customer_id",
          is_required: true,
          is_identifier: true,
          value_schema: {kind: "string", max_length: 128}
        },
        {
          api_name: "status",
          is_required: true,
          value_schema: {
            kind: "string",
            enum: ["prospect", "active", "inactive", "churned"]
          }
        }
      ]
    }')")

CUSTOMER_ID=$(jq -r '.id' <<<"${CUSTOMER}")

SUPPORT_CASE=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/registry/v1/artifacts/ontology_object_type" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg workspace_id "${WORKSPACE_ID}" \
    --arg ontology_id "${ONTOLOGY_ID}" \
    '{
      name: "support-case",
      workspace_id: $workspace_id,
      ontology_id: $ontology_id,
      api_name: "support_case",
      object_kind: "entity",
      implements: [],
      properties: [
        {
          api_name: "case_id",
          is_required: true,
          is_identifier: true,
          value_schema: {kind: "string", max_length: 128}
        }
      ]
    }')")

SUPPORT_CASE_ID=$(jq -r '.id' <<<"${SUPPORT_CASE}")

CUSTOMER_CASE_LINK=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/registry/v1/artifacts/ontology_link_type" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg workspace_id "${WORKSPACE_ID}" \
    --arg ontology_id "${ONTOLOGY_ID}" \
    --arg customer_id "${CUSTOMER_ID}" \
    --arg support_case_id "${SUPPORT_CASE_ID}" \
    '{
      name: "customer-has-support-case",
      workspace_id: $workspace_id,
      ontology_id: $ontology_id,
      api_name: "customer_has_support_case",
      source: {
        kind: "ontology_object_type",
        artifact_id: $customer_id
      },
      target: {
        kind: "ontology_object_type",
        artifact_id: $support_case_id
      },
      source_cardinality: "one",
      target_cardinality: "many",
      inverse_api_name: "support_case_belongs_to_customer",
      properties: []
    }')")
```

The link type is a logical definition, not merely a Registry relationship. It
has its own stable identity, endpoint types, two-sided cardinality, inverse
name, version history, and optional properties.

## Pin the candidate versions

Build a source manifest from the create responses. Publication never resolves
`latest` independently for each resource.

```bash theme={null}
SOURCE_VERSIONS=$(jq -n \
  --arg customer_id "$(jq -r '.id' <<<"${CUSTOMER}")" \
  --argjson customer_version "$(jq '.version_ordinal' <<<"${CUSTOMER}")" \
  --arg customer_hash "$(jq -r '.content_hash_hex' <<<"${CUSTOMER}")" \
  --arg case_id "$(jq -r '.id' <<<"${SUPPORT_CASE}")" \
  --argjson case_version "$(jq '.version_ordinal' <<<"${SUPPORT_CASE}")" \
  --arg case_hash "$(jq -r '.content_hash_hex' <<<"${SUPPORT_CASE}")" \
  --arg link_id "$(jq -r '.id' <<<"${CUSTOMER_CASE_LINK}")" \
  --argjson link_version "$(jq '.version_ordinal' <<<"${CUSTOMER_CASE_LINK}")" \
  --arg link_hash "$(jq -r '.content_hash_hex' <<<"${CUSTOMER_CASE_LINK}")" \
  '[
    {
      kind: "ontology_object_type",
      artifact_id: $customer_id,
      version_ordinal: $customer_version,
      content_hash: $customer_hash
    },
    {
      kind: "ontology_object_type",
      artifact_id: $case_id,
      version_ordinal: $case_version,
      content_hash: $case_hash
    },
    {
      kind: "ontology_link_type",
      artifact_id: $link_id,
      version_ordinal: $link_version,
      content_hash: $link_hash
    }
  ]')
```

Keep this manifest with the change being reviewed. If any referenced draft
changes before publication, the request fails instead of silently publishing a
different model.

## Validate and publish

Validate the complete graph before assigning a release number:

```bash theme={null}
VALIDATION=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/ontology/v1/validations" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg ontology_id "${ONTOLOGY_ID}" \
    --argjson source_versions "${SOURCE_VERSIONS}" \
    '{ontology_id: $ontology_id, source_versions: $source_versions}')")

jq '{is_valid, diagnostics, resource_count}' <<<"${VALIDATION}"
```

Publish only when `is_valid` is `true`:

```bash theme={null}
RELEASE=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/ontology/v1/releases" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg ontology_id "${ONTOLOGY_ID}" \
    --argjson source_versions "${SOURCE_VERSIONS}" \
    '{
      ontology_id: $ontology_id,
      release: "1.0.0",
      source_versions: $source_versions
    }')")

RELEASE_ID=$(jq -r '.id' <<<"${RELEASE}")
jq '{id, release, manifest_digest, compatibility}' <<<"${RELEASE}"
```

The release embeds the complete canonical graph. Reading it later does not
depend on mutable draft heads:

```bash theme={null}
curl -sS --fail-with-body \
  "${ATLAN_GATEWAY_URL}/ontology/v1/releases/${RELEASE_ID}/schema" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" | \
  jq '{release_id, release, manifest_digest, resources: (.schema.resources | length)}'
```

<Warning>
  Do not create an `ontology_release` through generic Registry CRUD. Native
  publication is the only supported path because it resolves exact sources,
  validates compatibility, derives the predecessor, and computes the manifest
  digest before the release is committed.
</Warning>

## Retrieve bounded agent context

An agent usually needs a relevant subgraph, not the entire release. Query one
exact release and set explicit resource, traversal-depth, and token bounds:

```bash theme={null}
CONTEXT=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/ontology/v1/context-queries" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg release_id "${RELEASE_ID}" \
    '{
      release_id: $release_id,
      query: "customer support cases",
      include: ["object_types", "link_types"],
      max_resources: 4,
      max_depth: 2,
      max_tokens: 4000,
      diagnostics: true
    }')")

jq '{
  release_id,
  manifest_digest,
  resources,
  token_estimate,
  truncated,
  refinement_hints,
  diagnostics
}' <<<"${CONTEXT}"
```

Each returned resource includes `selected_because` and `depth`. Check
`truncated` and `refinement_hints` before treating the response as sufficient
for the task. The service filters search candidates to the selected release and
expands typed dependencies without mixing in newer drafts.

An MCP client reaches the same operation as `ontology__get_context`:

```json theme={null}
{
  "release_id": "ontology_release_01example",
  "query": "customer support cases",
  "include": ["object_types", "link_types"],
  "max_resources": 4,
  "max_depth": 2,
  "max_tokens": 4000,
  "diagnostics": true
}
```

See [Get bounded ontology context](/mcp/tools/ontology-get-context) for the
complete input contract, limits, and result behavior. The Ontology extension
also provides [Export an ontology release](/mcp/tools/ontology-get-export) and
[Preview an ontology import](/mcp/tools/ontology-get-import-preview).

## Export or preview an import

List the available adapter versions before choosing one:

```bash theme={null}
curl -sS --fail-with-body \
  "${ATLAN_GATEWAY_URL}/ontology/v1/adapters" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" | jq
```

Export the release through the documented OWL subset:

```bash theme={null}
OWL_EXPORT=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/ontology/v1/exports" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg release_id "${RELEASE_ID}" \
    '{
      release_id: $release_id,
      format: "owl",
      serialization: "turtle",
      adapter_version: "owl/1",
      profile: "owl2-dl-supported-subset",
      base_iri: "https://example.test/ontology/logical-model#"
    }')")

jq '{adapter_version, media_type, document_digest, report}' <<<"${OWL_EXPORT}"
```

The `document` field contains Turtle. The `report` states every approximation,
omission, or unsupported construct. OWL and RDF are interchange formats over
the native logical model; they do not change Registry storage or validation
semantics.

Previewing an import returns proposed logical resources and never mutates the
ontology:

```bash theme={null}
IMPORT_PREVIEW=$(curl -sS --fail-with-body \
  -X POST "${ATLAN_GATEWAY_URL}/ontology/v1/imports/preview" \
  -H "Authorization: Bearer ${ATLAN_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "$(jq -n \
    --arg ontology_id "${ONTOLOGY_ID}" \
    --arg document "$(jq -r '.document' <<<"${OWL_EXPORT}")" \
    '{
      ontology_id: $ontology_id,
      document: $document,
      format: "owl",
      serialization: "turtle",
      adapter_version: "owl/1",
      profile: "owl2-dl-supported-subset",
      base_iri: "https://example.test/ontology/logical-model#"
    }')")

jq '{apply_supported, proposed_resources, report}' <<<"${IMPORT_PREVIEW}"
```

`apply_supported` is `false` in this release. Review the proposal and create or
update drafts through Registry CRUD; publish them through the native release
operation. The adapters never fetch remote imports.

## Verify the release boundary

Before handing the release to an agent or another service, verify:

* The validation response has `is_valid: true` and no error diagnostics.
* The published `manifest_digest` matches schema reads and context responses.
* Context retrieval names the intended `release_id` and stays within its bounds.
* `truncated` is false, or the caller follows the returned refinement hints.
* An RDF or OWL consumer accepts the adapter's profile and loss report.

## Current boundaries

The first release models definitions, not business records. It includes no
object-instance graph, physical table or column bindings, semantic SQL planner,
action execution, credentials, remote import fetching, or product-editor
schema. Those systems may refer to stable ontology identities in the future;
they do not belong inside the logical model.

<CardGroup cols={2}>
  <Card title="API reference" icon="code" href="/developers/api-reference">
    Inspect the released request, response, and problem contracts.
  </Card>

  <Card title="MCP" icon="plug" href="/mcp/overview">
    Connect an agent host and inspect the available Ontology tools.
  </Card>
</CardGroup>
