Data Contracts
A data contract is the agreement between the team that produces a dataset and the people or systems that consume it. It describes the ownership, structure, semantics, quality, and terms of use of the data — so consumers know exactly what they are getting and can trust it.
Data contracts are the core of Entropy Data: every output port of a data product is specified by a data contract, which is what turns a raw endpoint into something a consumer can rely on.
For a conceptual introduction — why data contracts exist, the contract-first approach, and the problems they solve — see Entropy Data's explainer What is a data contract?.
What's in a data contract
A data contract captures five dimensions of an agreement:
- Ownership — which team is responsible for the data and how to reach them.
- Schema — the structure: models, fields, types, and relationships.
- Semantics — what the data means, linked to your business definitions.
- Quality — the rules the data is guaranteed to meet, and how they are tested.
- Terms of use — pricing, SLAs, licensing, and the conditions for accessing the data.
The Open Data Contract Standard (ODCS)
Entropy Data uses the Open Data Contract Standard (ODCS) — a machine-readable YAML format — as the specification for its data contracts. ODCS is an open standard governed by Bitol, a Linux Foundation AI & Data project; it originated as the "Data Contract Template" used at PayPal. Entropy Data supports ODCS v3.
For an overview of the standard and how Entropy Data implements it, see Open Data Contract Standard. The full specification lives in the Bitol ODCS repository.
The older Data Contract Specification from datacontract.com has been merged into ODCS and is now deprecated — the datacontract.com team contributed it to Bitol, and the Data Contract CLI defaults to ODCS. Entropy Data continues to support the legacy specification until the end of 2026; new contracts should use ODCS.
ODCS building blocks
An ODCS contract is organized into a few top-level sections:
- Fundamentals — id, name, version, status, and other metadata.
- Schema — the models and their fields, including complex (nested) structures.
- Data quality — quality rules attached to the schema, runnable as tests.
- SLAs — service-level expectations such as freshness and availability.
- Servers — the physical locations of the data across environments.
- Roles — access roles used for role-based access control.
- Pricing and custom properties — optional cost and organization-specific fields.
Refer to datacontract.com and the ODCS specification for the full field-level reference.
Data quality
Data quality rules are the promises a contract makes about its data — the part that turns a schema into something consumers can trust. In ODCS, quality rules are attached to the schema, at the level of a model or an individual field.
Rules range from simple library checks (row counts, uniqueness, not-null) to custom SQL checks that express a business invariant. For example, the seeded Orders contract carries checks such as Row Count, No Duplicate Order IDs, and Previous Order Referential Integrity. The available rule types and their exact YAML are defined by ODCS — see the ODCS data quality reference and datacontract.com.
Quality rules are not just documentation: Entropy Data runs them against the real data behind an output port and records pass/fail results on the contract. See Test a data contract.
Those results can be summarised as a single mark out of 100, per contract and across your organization — see Data Quality Score.
Data contracts in Entropy Data
Within Entropy Data, a data contract is a living artifact you create, edit, test, and version:
- Specifies output ports — a contract is linked to a data product's output port and is what consumers read before they request access.
- Authored in the Data Contract Editor — edit a contract through its diagram, form, and YAML views in the browser-based Data Contract Editor (the default for ODCS contracts), with live validation. See Edit a data contract.
- Created from a source — derive the schema from an existing asset (or import from Excel) instead of typing it by hand, then refine it in the editor. See Put data under contract.
- Tested against real data — run the contract's data quality rules in the UI or with the Data Contract CLI, locally or in CI/CD. See Test a data contract.
- Versioned — manage breaking changes with major versions. See Version data contracts.
- Branched — draft a larger change, or let a colleague or an agent propose one, on a branch that lands on the contract when it is merged.
- Synced with Git — keep contracts in your repository and auto-push changes, or edit them as code.
- Classified — the contract header shows one classification badge per scheme, and each schema column shows its own classification or the one inherited from its linked semantic concept.
- Rich descriptions — description fields support Markdown and HTML. See Rich Text Formatting.
Data contract IDs
Every data contract has an id that is unique within the organization and must not contain whitespace. It is the identifier in the contract's URL and in the API, and what a data product's output port references. Where the ID comes from depends on how the contract is created:
| Created… | ID |
|---|---|
| In the Data Contract Editor | A suggestion derived from the data product's name — lowercased, spaces become -, umlauts are spelled out (ä → ae), other special characters are removed (e.g. orders), and -2, -3, … is appended if the ID is taken. Without a data product it is my-data-contract. You can change it before saving. |
| Via the API, Git, or the CLI | Nothing is generated — the ID is the one you send in PUT /api/datacontracts/{id}, which must match the id in the document. |
| From an asset | A random UUID. |
| From Excel | The id in the workbook, or a random UUID if it has none. |
| As a new major version | The base ID with the next major version as suffix, e.g. orders → orders_v2. A UUID gets a new UUID instead. See Version data contracts. |
To have the editor suggest a random UUID instead of a name-derived ID, set the uuid generation strategy in the customizations:
dataContract:
root:
standardProperties:
- property: "id"
generationStrategy: "uuid"
The ID of a data contract that is used by a data product cannot be changed; remove those references first.
Branches
A branch is a working copy of a data contract where changes are made before they land. Use it to draft a larger change, to let a colleague or an agent propose one, or to review a set of edits as a whole before the contract changes. The Branch dropdown in the contract header in Studio lists every branch; New branch cuts one from the contract as it is now. Only ODCS contracts can branch.
On a branch you work the way you work on the contract: the editor and the YAML editor are there, and both write to the branch. The contract itself does not change until the branch is merged. What the branch did is marked on every row: + for a schema object, property, quality rule, server, role, SLA entry, support channel, team member, verified statement, constraint, tag or custom property the branch adds, a pencil for one it edits, − for one it removes, and every section heading carries one mark for what happened inside it. A removed element stays on the page, washed out, so a reader sees what goes away; an edited description shows the old text struck through above the new one, and a badge such as the version, the status, a column's type or its classification names the old value in its mark's tooltip. The All / Changes toggle in the band switches between the whole document and the changes alone, each under its heading and nothing else. The output ports, consumers and cards that belong to the contract stay the contract's on a branch page; the schema drift check, which judges the assets against the contract, is not shown there.
A branch has its own test results: Run Data Contract Test on the branch page, or datacontract test --publish with the branch named in the upload, tests the branch's document. Those runs show on the branch only, never count for the contract, its notifications or the quality score, and go with the branch when it is merged or deleted.
The band on the branch page holds everything a branch can be done to:
- Merge to main lands every change on the contract and deletes the branch. A branch lands in place, so it can only carry a minor or a patch change: the version it lands with must be above the contract's within the same major, or the contract's own if the change is not worth a version. When the branch is still at the contract's version, the merge asks for the version to land with, keep, patch or minor, and preselects the step the branch's changes call for: wording and metadata alone suggest a patch, added or restructured elements a minor step. A removed or retyped column, table or server breaks consumers and calls for a new major version, which a branch cannot carry: the dialog says so and leaves the choice to you. A branch that changed the major version cannot be merged, because a breaking change is a new contract, cut with Create X.0.0 in the Version Management card. The same suggestion is on the branch's API resource as
suggested_stepandsuggested_version. ODPS defines an output port'sversionas the version of the contract it serves, so the dialog offers to set the new version on the output ports implementing the contract; the ports belong to other data products, so they are left alone unless you tick it. Merging takes what editing the contract takes: the edit permission on the owning team, or the organization owner role. - Actions > Update from main replays what changed on the contract since the branch was cut into the branch. It is offered while the contract has moved; merging does the same update first.
- Actions > Edit and Edit YAML edit the branch, YAML diff shows the whole branch against the contract side by side, and Delete branch discards it.
When the contract and the branch changed the same element, updating from main stops and marks the branch as Conflicted: the band shows a badge, the branch dropdown a warning icon, and the branch cannot be merged. Resolve conflicts in the band opens the two versions of the conflicting elements side by side; choose Use version from this branch or Use version from main for all of them, and the branch is up to date again.
Branches and Git
If the contract is connected to Git, each branch is mirrored as a Git branch named entropy-data/<contract id>/<branch>, holding the branch's document under the contract's id. Cutting the branch pushes that document, so does every edit and every update from main, and deleting the branch deletes it again. The band on the branch page shows how the branch stands against its Git branch: Changed in Git with Pull from Git, or Changed here and in Git with Resolve. A webhook ping pulls a Git branch that moved on its own into its branch automatically.
What Merge to main does depends on the contract's Git branch. The merge dialog offers two routes, the way the contract's own push dialog does: land here, so the contract reaches Git according to the connection's auto-push setting, or Create pull request (Create merge request on GitLab), which pushes the branch to Git and opens a request from it for review there. If the Git branch is protected, or the connection's auto-push mode is push to pull request, only the request route is open and the band's button says so. The branch stays open here until the request is merged or closed in Git; the merged document reaches the contract through the webhook or Pull on the contract page. See Git Integration.
Branches via the API
Branches are available under /api/datacontracts/{id}/branches/{branch}: PUT cuts a branch, GET and PUT …/datacontract.yaml read and replace the branch's document, GET …/changes lists what the branch changed, POST …/rebase updates it from the contract, POST …/merge lands it — with the version to land with in the body where the branch's own is not above the contract's, the contract's own version to keep it, and "update_port_versions": true to set the landed version on the implementing output ports — or opens a pull request where the contract's Git branch only takes reviewed changes or the body asks for it with "route": "pull_request", and DELETE discards it. A test result uploaded to /api/test-results with a branch lands on that branch, and so does one posted to …/branches/{branch}/test-results, which is where the branch page's How to test snippet points the CLI's --publish: the datacontract CLI can only be given a URL, and the path names the branch. GET on that path lists the branch's runs, as does GET /api/test-results?dataContractId=…&branch=…; without a branch, a data contract's listing and the organization's stay the data contract's. A branch's runs are deleted with the branch. POST …/branches/{branch}/test runs a server-side test against the branch's document, its result landing on the branch, and POST …/branches/{branch}/generate generates code from it, the way the branch page's buttons do. See the usage examples.
Managing data contracts via API
To create, read, update, and test data contracts programmatically, use the REST API — see the usage examples and the OpenAPI Specification.
Learn more
- What is a data contract? — concepts and rationale
- Open Data Contract Standard — the ODCS standard, explained
- datacontract.com — data contract standards, tooling, and the Data Contract CLI
- Bitol ODCS specification — the formal specification