Navigation: Home · Publishing guide · API docs

How to Publish an Ontology

A practical checklist for building OWL ontologies that are interoperable, resolvable, and maintainable. These are the things askwol checks, and why they matter.

Want a worked example? The askwol sample ontology applies every convention below and passes all checks. Load it from the home page (the sample ontology button) to see a clean report.

Two tags mark where a rule comes from, and two colored boxes flag extra context:

Spec what an OWL, RDF, RDFS, or other W3C/IETF specification actually defines or requires.

TDCC guideline a recommendation from the TDCC ontology engineers that askwol checks for, but that goes beyond what any specification mandates. A W3C Note (such as Cool URIs, cited below) is informative, not normative, so guidance drawn from one counts as a TDCC guideline here too.

A green box like this one is a tip or extra detail.
An amber box like this one is a warning or common pitfall to watch out for.
Contents Checks askwol runs (same order as the report) 1. Ontology basics 2. Namespaces & reuse 3. Term structure 4. Term documentation 5. Logic Additional best practices (no automated check)

1. Ontology basics

1.1 Ontology metadata

Give the ontology itself good metadata

Your ontology is itself a published research object. It should say what it is, who made it, which version it is, and under which license it can be reused.

Spec rdf:type owl:Ontology and owl:versionIRI/owl:versionInfo come from OWL 2; dcterms:title, dcterms:description, dcterms:creator, dcterms:created, dcterms:modified, and dcterms:publisher are defined by the DCMI Metadata Terms vocabulary, not OWL itself. Licensing gets its own dedicated check; see Open license below.

TDCC guideline Neither spec says any of these properties is mandatory on an ontology header. The Required / Recommended split below, and askwol’s choice to fail on the first group and only warn on the second, is this guide’s own editorial judgement of what a well-published ontology needs.

askwol evaluates SHACL shapes for the ontology header and checks these properties:

Pre-2010 ontologies (e.g. FOAF, OWL-Time) often use the older Dublin Core Elements (dc:) properties instead of DCMI Terms; askwol accepts either, since they carry the same meaning. One nuance: Elements 1.1’s single, generic dc:date property doesn’t distinguish created from modified, so askwol accepts it for either (or both) of the two recommended date checks above.

Fill in these metadata so that both humans and machines can understand the provenance and reuse conditions of your ontology.

Versioning

Version information is part of good ontology metadata. Spec OWL 2 defines owl:versionIRI to give each release its own dereferenceable URL, and owl:versionInfo to hold “a string that describes the IRI’s version,” with no format required.

TDCC guideline This guide recommends Semantic Versioning (MAJOR.MINOR.PATCH) as a practical convention, not an OWL requirement: bump major when you remove or redefine a term, minor when you add terms without breaking existing ones, and patch for fixes such as corrected labels or comments.

<http://example.org/my-ontology> a owl:Ontology ;
    owl:versionIRI <http://example.org/my-ontology/2.0.0> ;
    owl:versionInfo "2.0.0" .

Some ontologies version by date instead, especially W3C specifications on a fixed release schedule: PROV-O’s owl:versionIRI is http://www.w3.org/ns/prov-o-20130430. A date is simpler than semver but only says when something changed, not whether it is safe to upgrade. Pick one scheme per ontology and stick to it.

1.2 Imports

Declare imports, and keep them resolving

Spec If your ontology uses terms from another vocabulary, OWL 2 says to declare it with owl:imports in the ontology header:

<https://example.org/my-ontology> a owl:Ontology ;
    owl:imports <http://xmlns.com/foaf/0.1/> .

This tells reasoners and tools where your external terms are defined, so they can load the imported ontology and reason over it together with yours.

TDCC guideline Deciding which vocabularies deserve a formal import is a modelling judgement call askwol does not second-guess. What it does check is whether every owl:imports target you already declared is actually reachable: each IRI is fetched over HTTP and parsed as RDF, the same way a reasoner would follow it. This reachability check is askwol’s own addition; OWL 2 does not require import targets to be checked at authoring time.

A broken import (a dead link, a renamed URL, a server that no longer serves RDF) degrades reasoning for anyone who loads your ontology, with nothing reporting the failure unless a tool checks it explicitly.
askwol only checks that declared imports resolve; it never merges their content into the checks that run on your ontology. Every other check, including the reasoner, runs on your ontology document alone.

1.3 IRI strategy

Pick one IRI strategy (hash or slash) and stick to it

What askwol checks: every term defined inside your ontology’s own namespace is classified as either hash style (http://example.org/ont#Person) or slash style (http://example.org/ont/Person), per declared owl:Ontology base IRI. The check passes when all defined terms under one base IRI use the same pattern and warns when a single base IRI mixes both. An ontology that legitimately bundles more than one base IRI (a rare pattern, e.g. the W3C PROV family) may use a different, consistent style for each without being flagged.

Mixing hash and slash in the same vocabulary is almost always accidental. It breaks naive prefix-based namespace splitting and confuses consumers about whether /Person is the same term as #Person.

Hash vs. slash, in plain terms

TDCC guideline Neither RDF nor OWL requires hash or slash IRIs; both are valid (the W3C “Cool URIs for the Semantic Web” Interest Group Note describes both). They differ in how the identifier behaves over HTTP and how the vocabulary scales.

Recommendation

TDCC guideline If in doubt, go with slash: it scales. Use hash only for a vocabulary you know will stay small. The Cool URIs note concludes:

“Hash URIs should be preferred for rather small and stable sets of resources that evolve together. The ideal case are RDF Schema vocabularies and OWL ontologies. […] 303 URIs may also be used for [large] data sets, making neater-looking URIs.”

Either way, pick one per base IRI and don’t mix styles within it. Use a domain you control, or a persistent ID service like w3id.org or purl.org, so your IRIs survive domain changes.

1.4 IRI scheme (http vs https)

Use http or https, but be consistent per host

What askwol checks: every IRI used in the ontology (in subject, predicate, or object position, plus every bound namespace) is grouped by host. The check passes when each host appears under exactly one URI scheme and warns when the same host is referenced under both http:// and https://. The report lists the conflicting hosts with examples of each scheme so you can pick one canonical form and migrate the others.

Spec http://example.org/ont/Person and https://example.org/ont/Person are different IRIs: RDF identifies resources by their exact IRI string, per RFC 3987, and OWL never treats two IRIs as the same resource unless you assert owl:sameAs yourself. Mixing schemes breaks joins that rely on exact IRI matches, including SPARQL and any tool that does string comparison on URIs.

The legacy problem

Most foundational vocabularies were minted before HTTPS was ubiquitous. OWL, RDFS, FOAF, Dublin Core, SKOS and PROV-O all use http://. Changing them would break billions of existing triples, so they stay on http.

Do not “fix” http://xmlns.com/foaf/0.1/name to https://. You would be creating a different IRI that matches nothing in other people’s data.

For your own ontology

Rule of thumb: copy-paste the namespace IRI from the vocabulary’s own ontology file. Don’t retype it, don’t “upgrade” the scheme.

1.5 Open license

Adopt an open license

TDCC guideline Your ontology should be released under an open license; this guide recommends the DCMI property dcterms:license. To meet the Linked Open Data principles, the license must satisfy the Open Definition: “anyone can freely access, use, modify, and share [the ontology] for any purpose (subject, at most, to requirements that preserve provenance and openness).”

A license is open if it’s equivalent to the public domain, or at most adds attribution or share-alike restrictions; non-commercial or no-derivatives clauses are not open. For maximum reuse, this guide recommends one of the two Creative Commons licenses below.

  1. Creative Commons Zero (CC0) (public domain); IRI: https://creativecommons.org/publicdomain/zero/1.0/
  2. Creative Commons Attribution (CC BY) (requires attribution); IRI: https://creativecommons.org/licenses/by/4.0/

askwol also recognizes other licenses that meet the Open Definition but are less suitable for ontologies, for the reasons below.

Declare it with dcterms:license in the ontology header:

<https://example.org/my-ontology> a owl:Ontology ;
    dcterms:license <https://creativecommons.org/publicdomain/zero/1.0/> .
askwol checks each declared license against the Open Definition’s list of conformant licenses: a missing or non-open license is an error; an open but not-recommended license, or more than one declared license (even if each is fine on its own), is a warning.

2. Namespaces & reuse

2.1 Namespaces

Make namespaces resolvable

A namespace is the URI that a prefix expands to. In @prefix foaf: <http://xmlns.com/foaf/0.1/> the prefix is foaf and the namespace is http://xmlns.com/foaf/0.1/. This check looks at those namespace URIs; the short prefix aliases themselves are covered by Unused prefixes below.

TDCC guideline Every namespace URI should return something useful when fetched with HTTP. Ideally it returns RDF (content-negotiated), so tools can discover term definitions automatically. Neither RDF nor OWL requires a namespace to be dereferenceable; this expectation comes from the Linked Data principles and the W3C Note linked below.

Good: http://xmlns.com/foaf/0.1/ returns RDF when asked with Accept: application/rdf+xml.
Bad: A namespace that returns 404 or a generic HTML page with no RDF link.

If you host your own ontology, configure your server to support content negotiation, serving RDF to machines and HTML to browsers; see Server configuration below for the details.

2.2 Unused prefixes

Keep your prefixes clean

Spec Each @prefix line binds a short prefix to a namespace URI. TDCC guideline Turtle itself does not forbid declaring a prefix you never use; this check is about good hygiene: only declare prefixes you actually use. Leftover @prefix declarations clutter the file and confuse readers; they suggest a dependency that doesn’t exist.

@prefix dct: <http://purl.org/dc/terms/> .              # used below
@prefix geo: <http://www.opengis.net/ont/geosparql#> .  # unused, remove it
askwol flags every prefix that is declared but never appears in a triple, so you can clean them up.

2.3 External term definitions

Make sure reused terms actually exist

TDCC guideline Don’t reinvent the wheel. Before defining a new term, check if an established vocabulary already covers it. No spec requires reuse, but the wider Semantic Web interoperates far better when ontologies share terms:

When reusing a term, use the exact IRI from the source vocabulary. A typo like foaf:nme instead of foaf:name breaks interoperability.
askwol looks up every term you reuse from an external vocabulary and reports the ones that are not actually defined there. This catches typos like foaf:nme, made-up terms like owl:MadeUpClass, and namespace hijacking (asserting a brand-new term such as foaf:MadeUpConcept under someone else’s namespace instead of your own). Terms from your own namespace are checked separately (see Internal term definitions).
A term can also exist but be deprecated by the vocabulary that defines it. Spec owl:deprecated is an OWL 2 annotation property, and owl:DeprecatedClass/owl:DeprecatedProperty come from the OWL 1 Reference (used by e.g. VIVO). TDCC guideline A vs:term_status of “deprecated”/“archaic” is a community convention from a non-normative (W3C Note, used by e.g. FOAF), not a REC. askwol checks for all three and flags a deprecated reused term as a warning, so you don’t quietly build on a term the source vocabulary is phasing out.

3. Term structure

3.1 Internal term definitions

Define every term you use in your own namespace

TDCC guideline Every term you use from your own namespace should also be defined there. Don’t just use a term; define it. RDF’s open-world assumption actually permits referencing an IRI with no triples about it at all; askwol still flags it, because within your own namespace an undefined term is almost always an authoring mistake, not an intentional stub.

<#Person> a owl:Class ;
    rdfs:label "Person"@en ;
    rdfs:comment "A human being."@en .
If you reference ex:Persom but never define it, that’s probably a typo. askwol catches these.
askwol treats a term as defined when it appears as the subject of at least one triple, and as referenced when it appears as a predicate or object. A term in your own namespace that is referenced but never defined is flagged. External vocabulary terms are covered by the External term definitions check instead. Some ontologies re-declare a reused term as boilerplate (e.g. rdfs:label a owl:AnnotationProperty, as PROV-O and FOAF do). askwol correctly recognizes this as reuse of an existing term, keeping your own namespace scoped to what you actually define. New terms always belong in your own namespace: the External term definitions check confirms that anything asserted under an external prefix, like foaf:MadeUpConcept, genuinely exists there already, catching namespace hijacking along the way.

3.2 Term inventory & naming

Categorize your terms and name them consistently

Spec Every term you define falls into a category OWL 2 itself defines: a class, an object property, a datatype property, an annotation property, a datatype, or a named individual. askwol lists each internal term with the category it detected, so you can spot a term that was never typed (rdf:type missing) or typed as the wrong kind of thing.

<#Person>   a owl:Class .
<#hasParent> a owl:ObjectProperty .
<#birthDate> a owl:DatatypeProperty .

Naming conventions

TDCC guideline Capitalization is a community convention, not an OWL or RDFS rule; nothing stops you from naming a class person or a property HasName. askwol checks it anyway, because consistent casing is one of the fastest ways to tell a class from a property at a glance. A mismatch is reported as a warning, not a failure: it’s worth fixing, but it won’t block publication.

Some vocabularies use stable, coded identifiers instead: CIDOC CRM’s properties look like P2_has_type (the number is the permanent identifier; the wording after it can change between versions), and Wikidata’s are fully opaque, like P19, deliberately avoiding any one language’s naming bias. askwol recognizes this pattern, an uppercase letter directly followed by a digit, and does not flag it.

Object properties read best as verb phrases. A has or is prefix, or an of/by form, gives a single unambiguous reading: hasWife, wifeOf, lovedBy. This is a readability convention, not something askwol enforces; askwol only checks the case of the first letter, via SHACL shapes.
Mixing conventions (a lowercase class like person, or an uppercase property like HasName, with no digit following the capital) makes an ontology harder to read and to reuse.
A term marked deprecated (see External term definitions) is exempt from naming checks: an old term kept around for backward compatibility shouldn’t need renaming on its way out.

3.3 Domains & ranges

Give properties a domain and a range

Spec An rdfs:domain says what kind of subject a property applies to; an rdfs:range says what kind of value it takes. TDCC guideline RDFS does not require either one; declaring both is this guide’s recommendation because it makes a property self-documenting and lets tools reason about it.

<#hasParent> a owl:ObjectProperty ;
    rdfs:domain <#Person> ;
    rdfs:range  <#Person> .  # a class

<#birthDate> a owl:DatatypeProperty ;
    rdfs:domain <#Person> ;
    rdfs:range  xsd:date .   # a datatype
Spec In OWL, a domain or range is not a constraint that rejects bad data; a reasoner instead infers new facts from it. Stating rdfs:domain :Person on :birthDate tells a reasoner that anything with a :birthDate is a :Person. Pick domains and ranges that are actually true of every use.
askwol only looks at the rdfs:domain and rdfs:range declared directly on a property, not ones inherited from a super-property. A reasoner does follow that inheritance, so this check can occasionally flag something a reasoner would treat as fine. Checked against SHACL shapes. Deprecated properties (see External term definitions) are exempt.

3.4 Datatypes

Use recognized datatypes

Spec Datatype property ranges and typed literals can use any XSD built-in (xsd:string, xsd:integer, xsd:date, xsd:boolean, …), rdfs:Literal, rdf:langString, or a custom datatype you declare with rdfs:Datatype. TDCC guideline RDF places no closed list on datatype IRIs; any IRI is technically legal there. askwol checks against this recognized set instead, because an IRI outside it is almost never intentional.

<#age> rdfs:range xsd:nonNegativeInteger .
<#born> rdfs:range xsd:date .
"42"^^xsd:integer
A misspelled datatype (xsd:stirng, xsd:dateTiem) is treated as a brand-new, unknown datatype. Filters and validators that expect the real datatype then skip your values. askwol lists every datatype it sees and flags the ones it does not recognize.

3.5 Non-ontology terms

Keep concept schemes separate from the schema

TDCC guideline An OWL ontology is the schema: it defines classes, properties, and datatypes (the terminology). Named individuals are a different kind of thing, actual instances rather than schema, and most are fine to keep in the same file. SKOS concepts, the members of a controlled vocabulary or thesaurus, are the one case worth pulling out. A concept scheme is typically much larger than the schema and grows on its own schedule: new terms get added without ever touching a class or property definition. This guide recommends keeping SKOS concepts in their own SKOS concept scheme, separate from the ontology file, so schema and data can each evolve and be reused independently.

<#Dataset> a owl:Class .     # schema, belongs here
<#biology> a skos:Concept .  # concept, belongs in a SKOS scheme
<#Monday> a <#DayOfWeek> .   # named individual, fine to keep here
askwol only flags skos:Concept instances defined in your own namespace; a full concept scheme has usually outgrown the schema file and is better managed on its own. Named individuals are left alone: many ontologies deliberately define a small, fixed set of them alongside their schema (days of week, units of measure, as OWL-Time does), a legitimate modeling pattern rather than accidental instance data. External terms are ignored either way. Checked against SHACL shapes.

4. Term documentation

4.1 Labels

Give every class and property a human-readable label

TDCC guideline Every class and property you define should carry a human-readable rdfs:label: a short name a person can read. RDFS defines what the property means but does not require any resource to have one; this guide recommends adding one to everything you define.

<#hasMother> a owl:ObjectProperty ;
    rdfs:label "has mother"@en ;
    rdfs:domain <#Person> ;
    rdfs:range <#Person> .
Use language tags (@en, @de) to support multilingual ontologies. Consider skos:prefLabel and skos:altLabel for richer labeling.
askwol uses SHACL shapes to check that each internally defined class and property has an rdfs:label. Reused external vocabulary terms, and terms you have marked deprecated yourself, are ignored.

4.2 Comments

Give every class and property a clear description

TDCC guideline Every class and property you define should also carry an rdfs:comment: a brief description of what it means. As with labels, RDFS defines the property but does not require you to use it; this guide recommends it for every term you define.

<#hasMother> a owl:ObjectProperty ;
    rdfs:label "has mother"@en ;
    rdfs:comment "Relates a person to their biological mother."@en ;
    rdfs:domain <#Person> ;
    rdfs:range <#Person> .
A good comment states the intended meaning and, where helpful, how the term should (and should not) be used.
askwol uses SHACL shapes to check that each internally defined class and property has an rdfs:comment. Reused external vocabulary terms, and terms you have marked deprecated yourself, are ignored.
An inverse property (owl:inverseOf) doesn’t need its own comment when its partner already has one: the relationship is documented either way.

4.3 Language tag consistency

Use language tags consistently

Spec If your ontology includes human-readable labels and descriptions, tag every literal that carries natural-language text with a BCP 47 language tag (@en, @nl, @de, …), producing an RDF 1.1 language-tagged string.

:Person a owl:Class ;
    rdfs:label "person"@en ,
               "persoon"@nl ;
    rdfs:comment "A human being."@en ,
                    "Een menselijk wezen."@nl .

Consistency rules

TDCC guideline Neither RDF nor BCP 47 requires every subject to carry the same set of languages; askwol checks for it anyway, because an inconsistency here is usually a missed translation, not a deliberate choice:

If a literal has no language tag, SPARQL filters like FILTER(LANG(?label) = "en") won’t match it. The triple is still in your data, but any app or query that filters by language will skip it, as if it doesn’t exist.
askwol checks rdfs:label, rdfs:comment, skos:prefLabel, skos:definition, and other standard annotation properties for tag consistency on terms defined in your own namespace. Reused external vocabulary terms, and terms you have marked deprecated yourself, are ignored.

5. Logic

5.1 Reasoner checks

Check logical consistency

Spec OWL is a logical language. OWL 2 Direct Semantics formally defines when an ontology is consistent and when a class is satisfiable; a reasoner can derive consequences from your axioms and detect contradictions you didn’t intend. askwol reports these as three separate facets in the Reasoner checks section of the report:

TDCC guideline askwol runs a lightweight OWL 2 RL reasoner on the current ontology only; it does not follow owl:imports. This is an implementation tradeoff askwol makes, not a spec requirement: it catches the obvious self-contained contradictions without the cost of loading every imported vocabulary. For deeper checks (against imports, with HermiT or Pellet), use a desktop tool like Protégé.
This is the only check that reasons, and only over its own private copy of the graph. Every other check, including Domains & ranges, runs on the ontology as asserted; reasoning first could change some of those results.

Additional best practices

Validate early and often

Run askwol during development, not just before release

Run askwol on your ontology during development, not just before release.

Integrate validation into your CI pipeline: askwol check my-ontology.ttl

Server configuration

Serve it right: content negotiation

Once published, your ontology’s IRI should resolve: a client that dereferences it must get the RDF back. Two server-side settings make that work.

Keep the namespace IRI and the document URL consistent. In the askwol sample ontology, terms live under https://lod-4tu.tudelft.nl/dataset#, and dereferencing https://lod-4tu.tudelft.nl/dataset (no fragment) returns the ontology itself, directly or via redirect.
Static file servers often default to text/plain for .ttl. Set the MIME type explicitly in your web server or reverse proxy, otherwise namespace resolution (including askwol’s) fails.

Documentation

Generate human-readable documentation

Good rdfs:label and rdfs:comment annotations pay off once turned into a browsable HTML page: a documentation generator reads your Turtle file and produces one, ready to serve alongside the RDF via content negotiation.

pyLODE is a lightweight Python tool: a single command turns a Turtle file into one static HTML page that needs no server or JavaScript to view (example output).

python3 -m venv .venv && source .venv/bin/activate
python -m pip install pylode
pylode my-ontology.ttl -o my-ontology.html

WIDOCO is a Java-based documentation generator that produces a multi-page bundle: metadata extraction, an optional WebVOWL diagram, and a ready-to-deploy .htaccess for content negotiation.

java -jar widoco.jar -ontFile my-ontology.ttl -outFolder docs \
    -getOntologyMetadata -rewriteAll -uniteSections -webVowl -htaccess
WIDOCO’s WebVOWL view needs a web server to load; open the page over HTTP (python3 -m http.server 8080) rather than from the file system.

For W3C specification-style output, ReSpec renders technical documents in the browser, and OntoReSpec generates the markup it needs from an RDF ontology or data model (example). OntoReSpec is early-stage, with no stable release yet.