diff --git a/csvcubed/shacl/README.md b/csvcubed/shacl/README.md
new file mode 100644
index 000000000..800d40801
--- /dev/null
+++ b/csvcubed/shacl/README.md
@@ -0,0 +1,74 @@
+# SHACL Shapes
+
+**N.B. SHACL Shapes are a work-in-progress.**
+
+This directory contains a versioned history of the RDF shape of csvcubed's outputs.
+
+The purpose of these SHACL shape definitions is to provide a well-defined and versioned description of the RDF which is
+produced by csvcubed. These definition specify the interface (or API) between csvcubed and applications which may wish to
+process, consume or display the contents of CSV-Ws generated by the application.
+
+The SHACL shapes defined herein can also be used to check/validate whether a given CSV-W matches the csvcubed
+specifications. It is also possible to use [visualise]() these SHACL constraints to get an overview of the key
+RDF relationships.
+
+## Versioning
+
+Versioning of csvcubed's outputs uses a `MAJOR.minor` approach to versioning.
+
+**Any changes to the RDF structure of said output files must be accompanied by a new version in this directory.**
+
+### Major Versions
+
+Major versions are for any and all breaking changes, including defining new triples which fundamentally change
+how existing triples should be interpreted.
+
+### Minor Versions
+
+Minor versions may only **add** triple definitions which are **optional** and can safely be ignored by applications
+reading and interpreting CSV-Ws.
+
+## Folder Structure
+
+Each major version should have a `v*` folder with folders for each minor version inside, e.g. for version `1.0`,
+the following folder structure exists:
+
+```
+v1 - MAJOR version
+└── 0 - Minor version
+ └── ...SHACL shape files...
+```
+
+### Files
+
+Each version folder should:
+
+* Contain one JSON-LD file (at the top level) for each type of CSV-W output that csvcubed is capable of generating.
+ * For example a user should be able to locate the file defining a code-list's shape and validate their RDF directly against that.
+* Any shared or extraneous files should be defined in a sub-directory so as not to clutter up the definitions.
+
+## Visualisation
+
+The SHACL shapes defined in turtle format can be visualised using the [SHACL Play!](https://shacl-play.sparna.fr/play/draw) tool.
+
+## Validation
+
+### Apache Jena
+
+You can use the [Apache Jena shacl command](https://jena.apache.org/documentation/tools/#other-handy-command-line-tools)
+to validate SHACL shapes in turtle format against RDF data.
+
+
+```bash
+shacl validate --shapes shapes.ttl --data subsector.ttl
+```
+
+### pySHACL
+
+[pySHACL](https://github.com/RDFLib/pySHACL) supports directly validating RDF against SON-LD definitions:
+
+```bash
+pyshacl -s code-list.json subsector.ttl --imports --metashacl
+```
+
+N.B. The `imports` flag above is important when using the JSON-LD definitions since they are spread across multiple files.
\ No newline at end of file
diff --git a/csvcubed/shacl/v1/0/code-list.json b/csvcubed/shacl/v1/0/code-list.json
new file mode 100644
index 000000000..9b7fa05c9
--- /dev/null
+++ b/csvcubed/shacl/v1/0/code-list.json
@@ -0,0 +1,100 @@
+{
+ "@context": [
+ "deps/shacl-context.json",
+ {
+ "this": {
+ "@id": "https://purl.org/csv-cubed/shacl/v1.0/code-list.json#",
+ "prefix": true
+ },
+ "ui": "http://www.w3.org/ns/ui#",
+ "xkos": "http://rdf-vocabulary.ddialliance.org/xkos#",
+ "dcatshapes": {
+ "@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/dcat.json#",
+ "prefix": true
+ }
+ }
+ ],
+ "@id": "https://purl.org/csv-cubed/shacl/v1.0/code-list.json#ontology",
+ "@type": "owl:Ontology",
+ "owl:imports": "deps/dcat.json",
+ "shape": [
+ {
+ "@id": "this:ConceptSchemeShape",
+ "@type": "sh:NodeShape",
+ "targetClass": "skos:ConceptScheme",
+ "property": [
+ {
+ "path": "rdfs:label",
+ "name": "Code list's label",
+ "description": "The code list's label must be a non-zero length `rdf:languageString` with no duplicate languages present. Each label has a matching `dct:title` triple.",
+ "datatype": "rdf:langString",
+ "uniqueLang": true,
+ "minCount": 1,
+ "minLength": 1
+ },
+ {
+ "path": "xkos:variant",
+ "name": "Variant of",
+ "description": "The concept scheme is a variant of another existing code list",
+ "minCount": 0,
+ "nodeKind": "sh:IRI",
+ "class": "skos:ConceptScheme",
+ "node": "this:ConceptSchemeShape"
+ }
+ ],
+ "and": [
+ "dcatshapes:CatalogDatasetShape"
+ ]
+ },
+ {
+ "@id": "this:ConceptShape",
+ "@type": "sh:NodeShape",
+ "targetClass": "skos:Concept",
+ "property": [
+ {
+ "path": "rdfs:label",
+ "name": "Label",
+ "description": "A concept's label must be a non-zero length `xsd:string`.",
+ "minCount": 1,
+ "datatype": "xsd:string",
+ "minLength": 1
+ },
+ {
+ "path": "skos:notation",
+ "name": "Notation",
+ "description": "A concept must have a single notation. The notation must be a non-zero length `xsd:string`.",
+ "minCount": 1,
+ "maxCount": 1,
+ "datatype": "xsd:string",
+ "minLength": 1
+ },
+ {
+ "path": "skos:inScheme",
+ "name": "In Scheme",
+ "description": "A concept must be part of at least one concept scheme (code list). The concept scheme must be referred to by IRI.",
+ "minCount": 1,
+ "nodeKind": "sh:IRI",
+ "class": "skos:ConceptScheme",
+ "node": "this:ConceptSchemeShape"
+ },
+ {
+ "path": "ui:sortPriority",
+ "name": "Sort Priority",
+ "description": "A concept must have a single integer sort priority to guide user interfaces on the order in which to sort concepts for display.",
+ "minCount": 1,
+ "maxCount": 1,
+ "datatype": "xsd:integer"
+ },
+ {
+ "path": "skos:broader",
+ "name": "Broader Concept",
+ "description": "A concept may have one or more concepts which are broader than itself. These concepts must be referred to by IRI.",
+ "minCount": 0,
+ "nodeKind": "sh:IRI",
+ "class": "skos:Concept",
+ "node": "this:ConceptShape"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/csvcubed/shacl/v1/0/deps/dcat.json b/csvcubed/shacl/v1/0/deps/dcat.json
new file mode 100644
index 000000000..379e4250a
--- /dev/null
+++ b/csvcubed/shacl/v1/0/deps/dcat.json
@@ -0,0 +1,133 @@
+{
+ "@context": [
+ "shacl-context.json",
+ {
+ "this": {
+ "@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/dcat.json#",
+ "prefix": true
+ }
+ }
+ ],
+ "@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/dcat.json#ontology",
+ "@type": "owl:Ontology",
+ "shape": [
+ {
+ "@id": "this:CatalogDatasetShape",
+ "@type": "sh:NodeShape",
+ "targetClass": "dcat:Dataset",
+ "property": [
+ {
+ "path": "dct:title",
+ "name": "Title",
+ "equals": "rdfs:label",
+ "description": "A short title describing the catalogued item.",
+ "datatype": "rdf:langString",
+ "uniqueLang": true,
+ "minCount": 1,
+ "maxCount": 1,
+ "minLength": 1
+ },
+ {
+ "path": "dct:identifier",
+ "name": "Identifier",
+ "description": "An identifier for this catalogued item.",
+ "datatype": "xsd:string",
+ "minCount": 1,
+ "maxCount": 1,
+ "minLength": 1
+ },
+ {
+ "path": "dct:issued",
+ "name": "Date-time Issued",
+ "description": "The point in time when the catalogued item was first issued.",
+ "datatype": "xsd:dateTime",
+ "minCount": 1,
+ "maxCount": 1
+ },
+ {
+ "path": "dct:modified",
+ "name": "Date-time Modified",
+ "description": "The point in time when the catalogued item was last updated.",
+ "datatype": "xsd:dateTime",
+ "minCount": 1,
+ "maxCount": 1
+ },
+ {
+ "path": "rdfs:comment",
+ "name": "Comment",
+ "description": "Short sentence describing the item being catalogued.",
+ "datatype": "rdf:langString",
+ "uniqueLang": true,
+ "minCount": 0,
+ "maxCount": 1,
+ "minLength": 1
+ },
+ {
+ "path": "dct:description",
+ "name": "Description",
+ "description": "Longer multi-paragraph description of what this catalogued item represents.",
+ "datatype": "rdf:langString",
+ "uniqueLang": true,
+ "minCount": 0,
+ "maxCount": 1,
+ "minLength": 1
+ },
+ {
+ "path": "dct:license",
+ "name": "License",
+ "description": "The license under which the catalogue item is available.",
+ "nodeKind": "sh:IRI",
+ "minCount": 0,
+ "maxCount": 1
+ },
+ {
+ "path": "dct:creator",
+ "name": "Creator",
+ "description": "The original creator of the catalogued item.",
+ "nodeKind": "sh:IRI",
+ "minCount": 0,
+ "maxCount": 1
+ },
+ {
+ "path": "dct:publisher",
+ "name": "Publisher",
+ "description": "The publisher of the catalogued item.",
+ "nodeKind": "sh:IRI",
+ "minCount": 0,
+ "maxCount": 1
+ },
+ {
+ "path": "dcat:landingPage",
+ "name": "Landing Page",
+ "description": "Landing page where a copy of the catalogued item's data can be found for download.",
+ "nodeKind": "sh:IRI",
+ "minCount": 0
+ },
+ {
+ "path": "dcat:theme",
+ "name": "Theme",
+ "description": "Pre-defined theme URIs which the catalogued item sits within.",
+ "nodeKind": "sh:IRI",
+ "minCount": 0
+ },
+ {
+ "path": "dcat:keyword",
+ "name": "Keyword",
+ "description": "Free-text key words which catagorise the catalogued item.",
+ "datatype": "rdf:langString",
+ "uniqueLang": true,
+ "minCount": 0,
+ "minLength": 1
+ },
+ {
+ "path": "dcat:contactPoint",
+ "name": "Contact Point",
+ "description": "A URI which can be used to get in contact to discuss the catalogued item.",
+ "nodeKind": "sh:IRI",
+ "minCount": 0,
+ "maxCount": 1
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/csvcubed/shacl/v1/0/deps/shacl-context.json b/csvcubed/shacl/v1/0/deps/shacl-context.json
new file mode 100644
index 000000000..f8df88cca
--- /dev/null
+++ b/csvcubed/shacl/v1/0/deps/shacl-context.json
@@ -0,0 +1,44 @@
+{
+ "@context": [
+ "https://raw.githubusercontent.com/w3c/shacl/master/shacl-jsonld-context/shacl.context.ld.json",
+ {
+ "uniqueLang": {
+ "@id": "sh:uniqueLang",
+ "@type": "xsd:boolean"
+ },
+ "description": {
+ "@id": "sh:description",
+ "@type": "xsd:string"
+ },
+ "name": {
+ "@id": "sh:name",
+ "@type": "xsd:string"
+ },
+
+ "skos": "http://www.w3.org/2004/02/skos/core#",
+ "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
+ "dct": "http://purl.org/dc/terms/",
+ "owl": "http://www.w3.org/2002/07/owl#",
+ "dcat": "http://www.w3.org/ns/dcat#",
+
+ "@base": "https://purl.org/csv-cubed/shacl/v1.0/deps/shacl-context.json",
+ "@vocab": "https://purl.org/csv-cubed/shacl/v1.0/deps/shacl-context.json#",
+ "shape": {
+ "@type": "@id"
+ },
+ "owl:imports": {
+ "@type": "@id"
+ }
+ }
+ ],
+ "@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/shacl-context.json",
+ "rdfs:seeAlso": [
+ {
+ "@id": "#shape",
+ "@type": "rdf:Property",
+ "rdfs:label": "Defines a SHACL shape."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/csvcubed/shacl/v1/0/shapes-diagram.svg b/csvcubed/shacl/v1/0/shapes-diagram.svg
new file mode 100644
index 000000000..e96e13d6b
--- /dev/null
+++ b/csvcubed/shacl/v1/0/shapes-diagram.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/csvcubed/shacl/v1/0/ttl/README.md b/csvcubed/shacl/v1/0/ttl/README.md
new file mode 100644
index 000000000..ad8e81252
--- /dev/null
+++ b/csvcubed/shacl/v1/0/ttl/README.md
@@ -0,0 +1,3 @@
+# Turtle Shapes
+
+The parent directory's SHACL shapes defined in turtle format in a single file for ease of use.
diff --git a/csvcubed/shacl/v1/0/ttl/shapes.ttl b/csvcubed/shacl/v1/0/ttl/shapes.ttl
new file mode 100644
index 000000000..6a9902c68
--- /dev/null
+++ b/csvcubed/shacl/v1/0/ttl/shapes.ttl
@@ -0,0 +1,205 @@
+
+ .
+
+
+ a ;
+ "Defines a SHACL shape." .
+
+
+ a ;
+ ;
+ .
+
+
+ a ;
+ _:b0 .
+
+_:b0 ;
+ .
+
+
+ _:b1 ;
+ _:b2 ;
+ .
+
+_:b1 ;
+ "The code list's label must be a non-zero length `rdf:languageString` with no duplicate languages present. Each label has a matching `dct:title` triple." ;
+ 1 ;
+ 1 ;
+ "Code list's label" ;
+ ;
+ true .
+
+_:b2 ;
+ "The concept scheme is a variant of another existing code list" ;
+ 0 ;
+ "Variant of" ;
+ ;
+ ;
+ .
+
+
+ a ;
+ _:b3 ;
+ _:b4 ;
+ _:b5 ;
+ _:b6 ;
+ _:b7 ;
+ .
+
+_:b3 ;
+ "A concept's label must be a non-zero length `xsd:string`." ;
+ 1 ;
+ 1 ;
+ "Label" ;
+ .
+
+_:b4 ;
+ "A concept must have a single notation. The notation must be a non-zero length `xsd:string`." ;
+ 1 ;
+ 1 ;
+ 1 ;
+ "Notation" ;
+ .
+
+_:b5 ;
+ "A concept must be part of at least one concept scheme (code list). The concept scheme must be referred to by IRI." ;
+ 1 ;
+ "In Scheme" ;
+ ;
+ ;
+ .
+
+_:b6 ;
+ "A concept must have a single integer sort priority to guide user interfaces on the order in which to sort concepts for display." ;
+ 1 ;
+ 1 ;
+ "Sort Priority" ;
+ .
+
+_:b7 ;
+ "A concept may have one or more concepts which are broader than itself. These concepts must be referred to by IRI." ;
+ 0 ;
+ "Broader Concept" ;
+ ;
+ ;
+ .
+
+
+ a ;
+ .
+
+
+ a ;
+ _:b8 ;
+ _:b9 ;
+ _:b10 ;
+ _:b11 ;
+ _:b12 ;
+ _:b13 ;
+ _:b14 ;
+ _:b15 ;
+ _:b16 ;
+ _:b17 ;
+ _:b18 ;
+ _:b19 ;
+ _:b20 ;
+ .
+
+_:b8 ;
+ "A short title describing the catalogued item." ;
+ ;
+ 1 ;
+ 1 ;
+ 1 ;
+ "Title" ;
+ ;
+ true .
+
+_:b9 ;
+ "An identifier for this catalogued item." ;
+ 1 ;
+ 1 ;
+ 1 ;
+ "Identifier" ;
+ .
+
+_:b10 ;
+ "The point in time when the catalogued item was first issued." ;
+ 1 ;
+ 1 ;
+ "Date-time Issued" ;
+ .
+
+_:b11 ;
+ "The point in time when the catalogued item was last updated." ;
+ 1 ;
+ 1 ;
+ "Date-time Modified" ;
+ .
+
+_:b12 ;
+ "Short sentence describing the item being catalogued." ;
+ 1 ;
+ 0 ;
+ 1 ;
+ "Comment" ;
+ ;
+ true .
+
+_:b13 ;
+ "Longer multi-paragraph description of what this catalogued item represents." ;
+ 1 ;
+ 0 ;
+ 1 ;
+ "Description" ;
+ ;
+ true .
+
+_:b14 "The license under which the catalogue item is available." ;
+ 1 ;
+ 0 ;
+ "License" ;
+ ;
+ .
+
+_:b15 "The original creator of the catalogued item." ;
+ 1 ;
+ 0 ;
+ "Creator" ;
+ ;
+ .
+
+_:b16 "The publisher of the catalogued item." ;
+ 1 ;
+ 0 ;
+ "Publisher" ;
+ ;
+ .
+
+_:b17 "Landing page where a copy of the catalogued item's data can be found for download." ;
+ 0 ;
+ "Landing Page" ;
+ ;
+ .
+
+_:b18 "Pre-defined theme URIs which the catalogued item sits within." ;
+ 0 ;
+ "Theme" ;
+ ;
+ .
+
+_:b19 ;
+ "Free-text key words which catagorise the catalogued item." ;
+ 0 ;
+ 1 ;
+ "Keyword" ;
+ ;
+ true .
+
+_:b20 "A URI which can be used to get in contact to discuss the catalogued item." ;
+ 1 ;
+ 0 ;
+ "Contact Point" ;
+ ;
+ .
diff --git a/csvcubed/shacl/v1/0/ttl/to-turtle.sh b/csvcubed/shacl/v1/0/ttl/to-turtle.sh
new file mode 100755
index 000000000..54889844c
--- /dev/null
+++ b/csvcubed/shacl/v1/0/ttl/to-turtle.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# A script to build a single `shapes.ttl` files from the shapes defined in many JSON files.
+
+riot --syntax json-ld --out ttl ../deps/shacl-context.json > context.ttl || exit 1
+riot --syntax json-ld --out ttl ../deps/dcat.json > dcat.ttl || exit 1
+riot --syntax json-ld --out ttl ../code-list.json > code-list.ttl || exit 1
+
+riot --output=nt context.ttl code-list.ttl dcat.ttl | grep -v "http://www.w3.org/2002/07/owl#imports" | riot --syntax nt --output ttl > shapes.ttl
+
+rm -rf context.ttl dcat.ttl code-list.ttl
+