Your first model and query
Define two orders, bind a live database source and verify a published query
You need a Semogram account with access to the workspace and a project in it. Definition and binding changes, query publication, execution and assertion review require their respective permissions. External reads and writes also require the selected plugin installation's credentials and access.
What you will build
The model defines Order. A live Postgres endpoint contains two orders. Four virtual bindings map the Order class and its three properties to that endpoint. A published query returns two known rows. This first example uses live reads; it does not create a fact store or run a materialization pipeline.
| Name | Meaning |
|---|---|
operations-demo | A definition asset you create in this project |
https://example.com/operations#Order | The class identifier defined in model.ttl |
https://example.com/orders/1 | One actual order's entity identifier |
ontology_demo.demo_orders | The workspace endpoint you create |
public.ontology_demo_orders | The physical PostgreSQL table |
Use a dedicated test database and a project where this namespace does not conflict with an existing definition. You need definition, binding and query authoring permissions plus permission to publish and execute the query.
Prepare a known source
Use a test PostgreSQL database reachable from Semogram's execution runtime. In its SQL client, create the fixture below. These are database instructions, not text to paste into Semogram's endpoint form.
CREATE TABLE public.ontology_demo_orders (
entity_id text PRIMARY KEY,
order_id text NOT NULL,
status text NOT NULL,
total numeric(12,2) NOT NULL
);
INSERT INTO public.ontology_demo_orders VALUES
('https://example.com/orders/1', 'O-001', 'open', 120.00),
('https://example.com/orders/2', 'O-002', 'delivered', 75.50);
SELECT * FROM public.ontology_demo_orders ORDER BY entity_id;Open workspace Plugins → Explore, select Postgres (@craven/postgres) and install its read capability. Enter your database Host, Port (usually 5432), Database, User, Password and Ssl setting. The user needs SELECT access to the fixture. Enter credentials in the protected installation controls and check the connection. More options are explained in the Postgres installation guide.
Create a workspace Data Endpoint named demo_orders, namespace ontology_demo, direction Ingress, using that installed read capability and the source_stream contract. Set target Schema public, Table ontology_demo_orders. Save and inspect the two records. The endpoint name is chosen in Semogram; public.ontology_demo_orders is the physical database table. Keep the returned endpoint UUID. No import or materialization is needed for a live read.
Create the definition
Open project Ontology → Definitions. Create a definition named operations-demo with namespace https://example.com/operations#, model entrypoint model.ttl, no shape entrypoints and no imports. The file includes Customer and delivery terms for later expansion; this read uses Order only.
Use the definition authoring assistant to prepare the model below, or its supported model/file editor. Review the Turtle file, manifest, namespace and validation before saving. These are model files, not source records. Save the definition and inspect its active version and compiled project model.
Create an ontology definition named operations-demo in this project with namespace https://example.com/operations#. Use the model.ttl file shown on this page. Keep the full class and property identifiers. Show the manifest and validation before saving; do not import data or invent bindings.@prefix ex: <https://example.com/operations#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ex:Order a owl:Class ; rdfs:label "Order" .
ex:Customer a owl:Class ; rdfs:label "Customer" .
ex:orderId a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:string .
ex:status a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:string .
ex:total a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:decimal .
ex:delivered a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:boolean .
ex:isOpen a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:boolean .
ex:customerName a owl:DatatypeProperty ; rdfs:domain ex:Customer ; rdfs:range xsd:string .
ex:placedBy a owl:ObjectProperty ; rdfs:domain ex:Order ; rdfs:range ex:Customer .Use a workspace API key with ontologies:write. Set SEMOGRAM_API_KEY in your shell and replace UUID placeholders with accessible resource IDs.
curl --request POST "https://platform.semogram.com/api/v1/projects/<PROJECT_UUID>/ontology/definitions" \
--header "Authorization: Bearer ${SEMOGRAM_API_KEY}" \
--header "Idempotency-Key: <UNIQUE_OPERATION_KEY>" \
--header "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"manifest": {
"name": "operations-demo",
"description": "Orders, customers and delivery evidence",
"defaultNamespace": "https://example.com/operations#",
"modelEntrypoints": [
"model.ttl"
],
"shapeEntrypoints": [],
"imports": []
},
"files": [
{
"path": "model.ttl",
"content": "@prefix ex: <https://example.com/operations#> .\n@prefix owl: <http://www.w3.org/2002/07/owl#> .\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n\nex:Order a owl:Class ; rdfs:label \"Order\" .\nex:Customer a owl:Class ; rdfs:label \"Customer\" .\nex:orderId a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:string .\nex:status a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:string .\nex:total a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:decimal .\nex:delivered a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:boolean .\nex:isOpen a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:boolean .\nex:customerName a owl:DatatypeProperty ; rdfs:domain ex:Customer ; rdfs:range xsd:string .\nex:placedBy a owl:ObjectProperty ; rdfs:domain ex:Order ; rdfs:range ex:Customer .",
"contentType": "text/turtle"
}
],
"commitMessage": "Define the operations demo model"
}
JSON{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ontology_create",
"arguments": {
"projectId": "<PROJECT_UUID>",
"manifest": {
"name": "operations-demo",
"description": "Orders, customers and delivery evidence",
"defaultNamespace": "https://example.com/operations#",
"modelEntrypoints": [
"model.ttl"
],
"shapeEntrypoints": [],
"imports": []
},
"files": [
{
"path": "model.ttl",
"content": "@prefix ex: <https://example.com/operations#> .\n@prefix owl: <http://www.w3.org/2002/07/owl#> .\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n\nex:Order a owl:Class ; rdfs:label \"Order\" .\nex:Customer a owl:Class ; rdfs:label \"Customer\" .\nex:orderId a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:string .\nex:status a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:string .\nex:total a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:decimal .\nex:delivered a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:boolean .\nex:isOpen a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:boolean .\nex:customerName a owl:DatatypeProperty ; rdfs:domain ex:Customer ; rdfs:range xsd:string .\nex:placedBy a owl:ObjectProperty ; rdfs:domain ex:Order ; rdfs:range ex:Customer .",
"contentType": "text/turtle"
}
],
"commitMessage": "Define the operations demo model",
"idempotencyKey": "<UNIQUE_CREATE_KEY>"
}
}
}Get the compiled project package UUID and numeric version from the project's ontology/package inspection. Do not use the definition asset ID or saved version UUID in their place. Public API callers can POST /ontology/compile with ontologies:write to compile the active definitions, then inspect GET /ontology with ontologies:read for package details. Creating a definition compiles structure, not records.
Create four active bindings
In Ontology → Bindings, select the compiled package/version and the demo_orders endpoint. Create the following four bindings. Use the full identifier in Business term and Term identifier. Choose read method Virtual, status Active, and enter the shown resolution in the advanced Resolution editor.
Business term (namespace is https://example.com/operations#) | Applies to | Expected values | Resolution |
|---|---|---|---|
https://example.com/operations#Order | Class | Many | {"entity_id_field":"entity_id"} |
https://example.com/operations#orderId | Property | One | {"field":"order_id"} |
https://example.com/operations#status | Property | One | {"field":"status"} |
https://example.com/operations#total | Property | One | {"field":"total"} |
Leave access policy empty for this test only if your intended readers may read these values. Keep priority 100, default disabled, and select the actual package version. The class's source ID must be stable; a row number is not an entity identity.
Fill the fields in the table for each binding and validate before saving. A draft binding is not selected by query execution; each of these must be Active.
Create four active virtual read bindings for the compiled operations-demo model and ontology_demo.demo_orders. Bind the Order class to entity_id; bind orderId to order_id, status to status, and total to total. Use the full IRIs in this page and the actual compiled package/version. Show each resolution and validation before saving.Use a workspace API key with bindings:write. Set SEMOGRAM_API_KEY in your shell and replace UUID placeholders with accessible resource IDs.
curl --request POST "https://platform.semogram.com/api/v1/projects/<PROJECT_UUID>/ontology/read-bindings" \
--header "Authorization: Bearer ${SEMOGRAM_API_KEY}" \
--header "Idempotency-Key: <UNIQUE_OPERATION_KEY>" \
--header "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"ontologyPackageId": "<COMPILED_PACKAGE_UUID>",
"ontologyPackageVersion": 1,
"termRef": "https://example.com/operations#Order",
"termIri": "https://example.com/operations#Order",
"termKind": "class",
"kind": "virtual",
"dataEndpointId": "<SOURCE_ENDPOINT_UUID>",
"status": "active",
"cardinality": "many",
"resolution": {
"entity_id_field": "entity_id"
}
}
JSONRepeat this request for each property, replacing termRef, termIri, termKind, cardinality and resolution with its table entry. Replace numeric version 1 with the actual compiled version and use a new operation key per binding.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ontology_read_binding_create",
"arguments": {
"projectId": "<PROJECT_UUID>",
"binding": {
"ontologyPackageId": "<COMPILED_PACKAGE_UUID>",
"ontologyPackageVersion": 1,
"termRef": "https://example.com/operations#Order",
"termIri": "https://example.com/operations#Order",
"termKind": "class",
"kind": "virtual",
"dataEndpointId": "<SOURCE_ENDPOINT_UUID>",
"status": "active",
"cardinality": "many",
"resolution": {
"entity_id_field": "entity_id"
}
},
"idempotencyKey": "<UNIQUE_BINDING_KEY>"
}
}
}Repeat for the three property bindings using the values above and a new operation key each time.
Author and publish the query
In Ontology → Queries, create Demo orders. The manual authoring editor takes YAML; choose the assistant to prepare the same definition if preferred. Replace both package placeholders below with the real compiled UUID and numeric version before validating.
Enter the query name and the complete YAML below. Validate and save the draft. Review its current version, then Publish that exact version. Saving a draft alone does not make it executable.
Create and validate Demo orders using the YAML on this page. Select the actual compiled package/version and four virtual bindings. Return id, order_id, status and numeric total for every Order. Show validation and the draft version before publishing.query_definition:
name: Demo orders
slug: demo-orders
ontology_package_id: <COMPILED_PACKAGE_UUID>
ontology_package_version: <COMPILED_PACKAGE_VERSION_NUMBER>
target:
kind: virtual
input_schema:
type: object
additionalProperties: false
root:
term_ref: https://example.com/operations#Order
cardinality: many
output_schema:
type: object
properties:
id: { type: string }
order_id: { type: string }
status: { type: string }
total: { type: number }
required: [id, order_id, status, total]
output_mapping:
id: { term_ref: https://example.com/operations#Order }
order_id: { term_ref: https://example.com/operations#orderId }
status: { term_ref: https://example.com/operations#status }
total: { term_ref: https://example.com/operations#total }For HTTP callers, create the query with POST /ontology/queries and a body containing name, content (the complete YAML as a string) and authoringMode: "manual", using queries:write and an Idempotency-Key. MCP callers use query_create with those fields and idempotencyKey. Read the returned query ID and current saved version ID. Publish with the matching version:
Use a workspace API key with queries:publish. Set SEMOGRAM_API_KEY in your shell and replace UUID placeholders with accessible resource IDs.
curl --request POST "https://platform.semogram.com/api/v1/projects/<PROJECT_UUID>/ontology/queries/<QUERY_UUID>/releases" \
--header "Authorization: Bearer ${SEMOGRAM_API_KEY}" \
--header "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"expectedVersionId": "<REVIEWED_QUERY_VERSION_UUID>"
}
JSON{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "query_publish",
"arguments": {
"projectId": "<PROJECT_UUID>",
"queryId": "<QUERY_UUID>",
"expectedVersionId": "<REVIEWED_QUERY_VERSION_UUID>",
"idempotencyKey": "<UNIQUE_PUBLISH_KEY>"
}
}
}Execute and compare
Open the published query, choose Execute, use an empty input object and a page limit of 10. Inspect the execution result and diagnostics.
Execute the published Demo orders query with no inputs and limit 10. If it queues a job, wait for its completed result. Compare the returned rows with O-001/open/120 and O-002/delivered/75.50. Report completeness, consistency and the resolved source endpoint.Use a workspace API key with queries:execute. Set SEMOGRAM_API_KEY in your shell and replace UUID placeholders with accessible resource IDs.
curl --request POST "https://platform.semogram.com/api/v1/projects/<PROJECT_UUID>/ontology/queries/<QUERY_UUID>/execute" \
--header "Authorization: Bearer ${SEMOGRAM_API_KEY}" \
--header "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"input": {},
"options": {
"limit": 10
}
}
JSON{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "query_execute",
"arguments": {
"projectId": "<PROJECT_UUID>",
"queryId": "<QUERY_UUID>",
"request": {
"input": {},
"options": {
"limit": 10
}
}
}
}
}Expected values (compare by ID, rather than assuming row order):
| id | order_id | status | total |
|---|---|---|---|
https://example.com/orders/1 | O-001 | open | 120 |
https://example.com/orders/2 | O-002 | delivered | 75.5 |
A queued or running result is not a finished read. Continue with the returned job ID until it succeeds; then inspect data.rows, meta.completeness, meta.consistency, meta.sourceSnapshots, meta.resolvedBindings and diagnostics. The decimal formatting may differ from SQL; compare numeric values. Postgres live reads do not imply one transactional snapshot across several systems.
If validation says a term is unknown, compare the full IRI with the compiled model. If no binding resolves, check package/version, Active status, class identity field and property mappings. If execution fails, test the endpoint connection and database privileges. Do not fix missing bindings by copying credentials into the query.