
## Manage data contracts via SDKs

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/data-contracts/how-tos/manage-via-sdk

> Manage data contracts for assets in Atlan programmatically using the Python SDK (pyatlan).

# DataContract: manage data contracts via SDK

Use `DataContract` in the Atlan Python SDK to programmatically create, version, and manage data contracts on assets.

:::warning[Limited availability]
Data contracts can currently only be managed for
[tables](https://docs.atlan.com/llms/platform/types/table/llms.txt), [views](https://docs.atlan.com/llms/platform/types/view/llms.txt),
and [materialized views](https://docs.atlan.com/llms/platform/types/materialised-view/llms.txt).
:::

## Create new contract

To create a [contract](https://docs.atlan.com/llms/platform/python/data-contract-spec/llms.txt) for an existing asset in Atlan:

### Java

```java showLineNumbers title="Create a data contract"
Table asset = Table.updater("default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN", "SALE_TXN")
 .build();
String spec = client
 .contracts.generateInitialSpec(asset); // (1)
DataContractSpec dcs = DataContractSpec.fromString(spec) // (2)
 .toBuilder()
 .description("Changed description.")
 .extraProperty("something", "extra")
 .build();
DataContract contract = DataContract.creator(spec, asset) // (3)
 .build();
AssetMutationResponse response = contract.save(client); // (4)
```

1. Start by initializing a data contract. You can use the `.contracts.generateInitialSpec()` on any Atlan client to generate the initial YAML data contract specification for a given asset.
2. (Optional) You can translate the YAML string representation into a specification object that you can then programmatically extend, without needing to do direct string manipulations.

 :::warning[Loses all comments]
Be aware that doing this conversion will remove any comments in the YAML.
 :::
3. You need to provide the contract specification (YAML), as a string, and the asset the contract will govern to the `DataContract.creator()` method.

 :::tip[Converting an object into the string form]
If you programmatically modified the specification as an object, you can convert it back to its YAML string form simply by calling `.toString()` on the object. You are always asked to provide the YAML string form here to make sure that if you want to keep any comments, you have the option to do so (since the object form removes any comments).
 :::
4. Finally, you can call the `save()` method to create the new data contract in Atlan. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Create a data contract"
from pyatlan.model.assets import Table
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataContract
from pyatlan.model.contract import DataContractSpec

client = AtlanClient()

asset = Table.updater(
 qualified_name="default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN", 
 name="SALE_TXN"
)

spec = client.contracts.generate_initial_spec(asset) # (1)

contract_spec = DataContractSpec.from_yaml(spec) # (2)
contract_spec.description = "Changed description."
contract_spec.extra_properties = {"something" : "extra"}

contract = DataContract.creator( # (3)
 asset_qualified_name=asset.qualified_name,
 contract_spec=contract_spec,
)

response = client.asset.save(contract) # (4)
```

1. Start by initializing a data contract. You can use the `.contracts.generate_initial_spec()`
on any Atlan client to generate the initial YAML data contract specification for a given asset.
2. (Optional) You can translate the YAML string representation into a specification object
that you can then programmatically extend, without needing to do direct string manipulations.

 :::warning[Loses all comments]
Be aware that doing this conversion will remove any comments in the YAML.
 :::
3. You need to provide the contract specification (YAML), as a string,
and the asset the contract will govern to the `DataContract.creator()` method.
4. Finally, you can call the `save()` method to create the new data contract in Atlan.

### Kotlin

```kotlin showLineNumbers title="Create a data contract"
val asset = Table.updater("default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN", "SALE_TXN")
 .build()
val spec = client
 .contracts.generateInitialSpec(asset) // (1)
val dcs = DataContractSpec.fromString(spec) // (2)
 .toBuilder()
 .description("Changed description.")
 .extraProperty("something", "extra")
 .build()
val contract = DataContract.creator(spec, asset) // (3)
 .build()
val response = contract.save(client) // (4)
```

1. Start by initializing a data contract. You can use the `.contracts.generateInitialSpec()` on any Atlan client to generate the initial YAML data contract specification for a given asset.
2. (Optional) You can translate the YAML string representation into a specification object that you can then programmatically extend, without needing to do direct string manipulations.

 :::warning[Loses all comments]
Be aware that doing this conversion will remove any comments in the YAML.
 :::
3. You need to provide the contract specification (YAML), as a string, and the asset the contract will govern to the `DataContract.creator()` method.

 :::tip[Converting an object into the string form]
If you programmatically modified the specification as an object, you can convert it back to its YAML string form simply by calling `.toString()` on the object. You are always asked to provide the YAML string form here to make sure that if you want to keep any comments, you have the option to do so (since the object form removes any comments).
 :::
4. Finally, you can call the `save()` method to create the new data contract in Atlan. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title="POST /api/meta/entity/bulk"
{
"entities": [
 {
 "typeName": "DataContract", // (1)
 "attributes": { // (2)
 "dataContractJson": "{\"type\": \"Table\", \"status\": \"DRAFT\", \"kind\": \"DataContract\", \"dataset\": \"SALE_TXN\", \"data_source\": \"snowflake\", \"description\": \"Created by Python SDK.\", \"columns\": [{\"name\": \"order_id\", \"data_type\": \"BIGNUMERIC\", \"description\": \"\"}]}",
 "name": "Data contract for SALE_TXN", // (3)
 "qualifiedName": "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/contract" // (4)
 }
 }
]
}
```

1. The `typeName` must be exactly `DataContract`.
2. Provide the data contract JSON. In this example, we're creating it with only the minimal required properties as specified by the API. Please check the reference section for the complete [data contract specification](https://docs.atlan.com/llms/platform/python/data-contract-spec/llms.txt).

 - type of the asset in Atlan (`Table`, `View`, or `MaterializedView`).
 - state of the contract (`DRAFT` or `VERIFIED`).
 - must always be `DataContract`.
 - name of the asset as it exists inside Atlan.
 - name of the asset connection as it exists inside Atlan.
 - (Optional) description of this dataset, for documentation purposes.
 - (Optional) `columns`:
 - name of the column as it's defined in the source system (often technical).
 - physical data type of values in this column.
 - description of this column, for documentation purposes.

2. You must provide a human-readable name for your contract.
3. The `qualifiedName` should follow the pattern: `<assetQualifiedName>/contract`
(where `assetQualifiedName` is, in this example, the `qualifiedName` of a Snowflake table).

## Retrieve contract

### By asset:

To retrieve the latest contract and certified
contract of a given asset using its qualified name:

### Java

```java showLineNumbers title="Retrieve latest and certified data contract of a asset"
Table table = Table.get(client, "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN", true); // (1)
DataContract latest = table.getDataContractLatest(); // (2)
DataContract certified = table.getDataContractLatestCertified(); // (3)
```

1. First, retrieve the asset by its `qualifiedName`. Because this operation will retrieve the asset from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. Retrieve the latest data contract by using `.getDataContractLatest()`.
3. Retrieve the certified data contract by using the `.getDataContractLatestCertified()`.

### Python

```python showLineNumbers title="Retrieve latest and certified data contract of a asset"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataContract

client = AtlanClient()

table = client.asset.get_by_qualified_name( # (1)
 asset_type=Table,
 qualified_name="default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN"
)

latest_contract = table.data_contract_latest # (2)
certified_contract = table.data_contract_latest_certified # (3)
```

1. First, retrieve the asset by its `qualified_name`.
2. Retrieve the latest data contract by using the `table.data_contract_latest` attribute.
3. Retrieve the certified data contract by using the `table.data_contract_latest_certified` attribute.

### Kotlin

```kotlin showLineNumbers title="Retrieve latest and certified data contract of a asset"
val table = Table.get(client, "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN", true) // (1)
val latest = table.dataContractLatest // (2)
val certified = table.dataContractLatestCertified // (3)
```

1. First, retrieve the asset by its `qualifiedName`. Because this operation will retrieve the asset from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. Retrieve the latest data contract by using `.dataContractLatest`.
3. Retrieve the certified data contract by using the `.dataContractLatestCertified`.

### Raw REST API

```json showLineNumbers title="GET /api/meta/entity/uniqueAttribute/type/Table?attr%3AqualifiedName=default%2Fsnowflake%2F1717514525%2FRAW%2FWIDEWORLD%2FSALE_TXN&minExtInfo=False&ignoreRelationships=False"
// (1)
```

1. All details are in the URL itself.

 :::tip[URL-encoded filter]

Note that the filter is URL-encoded. [decoded it would be](https://www.urldecoder.org): `/api/meta/entity/uniqueAttribute/type/Table?attr:qualifiedName=default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN&minExtInfo=False&ignoreRelationships=False`
 :::

### By qualified name:

To retrieve a contract by its version (`V1`, `V2`, etc) using its qualified name:

### Java

```java showLineNumbers title="Retrieve a data contract by its version"
DataContract contract = DataContract.get( // (1)!
 client, "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/Table/contract/V1"
);
```

1. The `qualifiedName` of the data contract must be in the format: `<assetQualifiedName>/<assetType>/contract/V<versionNumber>`. For this example:
 - `assetQualifiedName`: `qualifiedName` of a Snowflake table.
 - `assetType`: type of this asset in Atlan, i.e: `Table`.
 - `versionNumber`: specific version of the data contract to retrieve, e.g: `1`, `2`, and so on.

### Python

```python showLineNumbers title="Retrieve a data contract by its version"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataContract

client = AtlanClient()

contract = client.asset.get_by_qualified_name(
 asset_type=DataContract, # (1)
 qualified_name="default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/Table/contract/V1"
)
```

1. The `qualifiedName` of the data contract must be in the format:
`<assetQualifiedName>/<assetType>/contract/V<versionNumber>`.
For this example:
 - `assetQualifiedName`: `qualifiedName` of a Snowflake table.
 - `assetType`: type of this asset in Atlan, i.e: `Table`.
 - `versionNumber`: specific version of the data
 contract to retrieve, e.g: `1`, `2`, and so on.

### Kotlin

```kotlin showLineNumbers title="Retrieve a data contract by its version"
val contract = DataContract.get( // (1)!
 client, "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/Table/contract/V1"
)
```

1. The `qualifiedName` of the data contract must be in the format: `<assetQualifiedName>/<assetType>/contract/V<versionNumber>`. For this example:
 - `assetQualifiedName`: `qualifiedName` of a Snowflake table.
 - `assetType`: type of this asset in Atlan, i.e: `Table`.
 - `versionNumber`: specific version of the data contract to retrieve, e.g: `1`, `2`, and so on.

### Raw REST API

```json showLineNumbers title="GET /api/meta/entity/uniqueAttribute/type/DataContract?attr%3AqualifiedName=dedefault%2Fsnowflake%2F1717514525%2FRAW%2FWIDEWORLD%2FSALE_TXN%2FTable%2Fcontract%2FV1&minExtInfo=False&ignoreRelationships=False"
// (1)
```

1. All details are in the URL itself.

 :::tip[URL-encoded filter]

Note that the filter is URL-encoded. [decoded it would be](https://www.urldecoder.org): `attr:qualifiedName=default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/Table/contract/V1&minExtInfo=False&ignoreRelationships=False`

where the `qualifiedName` of the data contract must be in the format:
`<assetQualifiedName>/<assetType>/contract/V<versionNumber>`.
For this example:

 - `assetQualifiedName`: `qualifiedName` of a Snowflake table.
 - `assetType`: type of this asset in Atlan, i.e: `Table`.
 - `versionNumber`: specific version of the data contract to retrieve, e.g: `1`, `2`, and so on.
 :::

## Update contract

In the following example, we're updating the contact
`certificateStatus` field to `VERIFIED` (shown as `PUBLISHED` in the UI):

### Java

```java showLineNumbers title="Update a data contract"
DataContractSpec updatedContractDetails = DataContractSpec.fromString(spec) // (1)
 .toBuilder()
 .status(DataContractStatus.VERIFIED) // (2)
 .build();
DataContract contract = DataContract.updater( // (3)
 "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/contract",
 "Data contract for SALE_TXN"
)
 .dataContractSpec(updatedContractDetails.toString()) // (4)
 .build();
AssetMutationResponse response = contract.save(client); // (5)
```

1. Begin by constructing the updated data contract specification. This example assumes you already have the string YAML form in a variable named `spec`, which you have retrieved from the data contract using one of the retrieval methods above.
2. After converting the specification into a builder (using `.toBuilder()`) you can chain any updates you want against it, such as changing its status.
3. Use the `updater()` method to update a data contract.

 - `qualifiedName` of the data contract, ie: `<assetQualifiedName>/contract` (where `assetQualifiedName` is, in this example, the `qualifiedName` of a Snowflake table).
 - `name` of the data contract. (`NOTE:` SDKs and [CLI](https://docs.atlan.com/llms/governance/contracts/manage-via-cli/llms.txt) always generate it in the format: **"Data contract for `dataset` (`asset.name`)"**).

4. You can then add any other updates or attributes. In this example, we're updating the contract spec itself (must be `string`).
5. To update the data contract in Atlan, call the `save()` method with the object you've built. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Update a data contract"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataContract
from pyatlan.model.contract import DataContractSpec
from pyatlan.model.enums import DataContractStatus

client = AtlanClient()

spec = current_contract.data_contract_spec

updated_contract_spec = DataContractSpec.from_yaml(spec) # (1)
updated_contract_spec.status = DataContractStatus.VERIFIED # (2)

contract = DataContract.updater( # (3)
 qualified_name="default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/contract",
 name="Data contract for SALE_TXN",
)
contract.data_contract_spec = updated_contract_spec.to_yaml() # (4)

response = client.asset.save(contract) # (5)
```

1. Begin by constructing the updated data contract specification.
This example assumes you already have the string YAML form in a
variable named `spec`, which you have retrieved from the data
contract using one of the retrieval methods above.
2. After converting the specification into `DataContractSpec`
instance, you can then chain any updates you
want against it, such as changing its `status`.
3. Use the `updater()` method to update a data contract.

 - `qualifiedName` of the data contract,ie: `<assetQualifiedName>/contract` (where `assetQualifiedName` is, in this example, the `qualifiedName` of a Snowflake table).
 - `name` of the data contract. (`NOTE:` SDKs and [CLI](https://docs.atlan.com/llms/governance/contracts/manage-via-cli/llms.txt) always generate it in the format: **"Data contract for `dataset` (`asset.name`)"**).

4. You can then add any other updates or attributes.
In this example, we're updating the contract spec itself
(make sure to use `.to_yaml()` to convert spec instance to YAML string)
5. To update the data contract in Atlan, call the `save()` method with the object you've built.

### Kotlin

```kotlin showLineNumbers title="Update a data contract"
val updatedContractDetails = DataContractSpec.fromString(spec) // (1)
 .toBuilder()
 .status(DataContractStatus.VERIFIED) // (2)
 .build()
val contract = DataContract.updater( // (3)
 "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/contract",
 "Data contract for SALE_TXN"
)
 .dataContractSpec(updatedContractDetails.toString()) // (4)
 .build()
val response = contract.save(client) // (5)
```

1. Begin by constructing the updated data contract specification. This example assumes you already have the string YAML form in a variable named `spec`, which you have retrieved from the data contract using one of the retrieval methods above.

 :::warning[Won't retain any comments]
Keep in mind that when programmatically building the specification as an object, no comments will be retained. If you want to have comments in your YAML specification, you must directly manipulate the YAML string yourself.
 :::
2. After converting the specification into a builder (using `.toBuilder()`) you can chain any updates you want against it, such as changing its status.
3. Use the `updater()` method to update a data contract.

 - `qualifiedName` of the data contract, ie: `<assetQualifiedName>/contract` (where `assetQualifiedName` is, in this example, the `qualifiedName` of a Snowflake table).
 - `name` of the data contract. (`NOTE:` SDKs and [CLI](https://docs.atlan.com/llms/governance/contracts/manage-via-cli/llms.txt) always generate it in the format: **"Data contract for `dataset` (`asset.name`)"**).

4. You can then add any other updates or attributes. In this example, we're updating the contract spec itself (must be `string`).
5. To update the data contract in Atlan, call the `save()` method with the object you've built. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title="POST /api/meta/entity/bulk"
{
"entities": [
 {
 "typeName": "DataContract", // (1)
 "attributes": { // (2)
 "dataContractJson": "{\"type\": \"Table\", \"status\": \"VERIFIED\", \"kind\": \"DataContract\", \"dataset\": \"SALE_TXN\", \"data_source\": \"snowflake\", \"description\": \"Created by Python SDK.\", \"columns\": [{\"name\": \"order_id\", \"data_type\": \"BIGNUMERIC\", \"description\": \"\"}]}",
 "name": "Data contract for SALE_TXN", // (3)
 "qualifiedName": "default/snowflake/1717514525/RAW/WIDEWORLD/SALE_TXN/contract" // (4)
 }
 }
]
}
```

1. The `typeName` must be exactly `DataContract`.
2. Provide the data contract JSON. In this example, we're updating it with only the minimal required properties as specified by the API. Please check the reference section for the complete [data contract specification](https://docs.atlan.com/llms/platform/python/data-contract-spec/llms.txt).

 - type of the asset in Atlan (`Table`, `View`, or `MaterializedView`).
 - state of the contract (`DRAFT` or `VERIFIED`).
 - must always be `DataContract`.
 - name of the asset as it exists inside Atlan.
 - name of the asset connection as it exists inside Atlan.
 - (Optional) description of this dataset, for documentation purposes.
 - (Optional) `columns`:
 - name of the column as it's defined in the source system (often technical).
 - physical data type of values in this column.
 - description of this column, for documentation purposes.

3. Human-readable name for your contract.
4. The `qualifiedName` of your contract, ie: `<assetQualifiedName>/contract`
(where `assetQualifiedName` is, in this example, the `qualifiedName` of a Snowflake table).

## Delete contract

### Soft-delete (archive)

To soft-delete, or archive, a contract:

### Java

:::warning[Coming soon]
:::

### Python

:::warning[Coming soon]
:::

### Kotlin

:::warning[Coming soon]
:::

### Raw REST API

:::warning[Coming soon]
:::

### Hard-delete (purge)

To permanently delete (purge) a contract:

### Java

:::warning[Coming soon]
:::

### Python

:::warning[Coming soon]
:::

### Kotlin

:::warning[Coming soon]
:::

### Raw REST API

:::warning[Coming soon]
:::

---
