Omni Model Builder
Create and modify Omni's semantic model through the YAML API — views, topics, dimensions, measures, relationships, and query views.
Tip: Always use omni-model-explorer first to understand the existing model.
Prerequisites
export OMNI_BASE_URL="https://yourorg.omniapp.co"
export OMNI_API_KEY="your-api-key"
You need Modeler or Connection Admin permissions.
API Discovery
When unsure whether an endpoint or parameter exists, fetch the OpenAPI spec:
curl -L "$OMNI_BASE_URL/openapi.json" \
-H "Authorization: Bearer $OMNI_API_KEY"
Use this to verify endpoints, available parameters, and request/response schemas before making calls.
Safe Development Workflow
Always work in a branch. Never write directly to production.
Step 1: Write YAML to a Branch
curl -L -X POST "$OMNI_BASE_URL/api/v1/models/{modelId}/yaml" \
-H "Authorization: Bearer $OMNI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fileName": "my_new_view.view",
"yaml": "dimensions:\n order_id:\n primary_key: true\n status:\n label: Order Status\nmeasures:\n count:\n aggregate_type: count",
"mode": "extension",
"branchId": "{branchId}",
"commitMessage": "Add my_new_view with status dimension and count measure"
}'
branchId is a UUID — retrieve it from the List Models endpoint with ?include=activeBranches. If the branch doesn't exist yet, Omni creates it.
Step 2: Validate
curl -L "$OMNI_BASE_URL/api/v1/models/{modelId}/validate?branchId={branchId}" \
-H "Authorization: Bearer $OMNI_API_KEY"
Returns validation errors and warnings with message, yamlpath, and sometimes autofix.
Step 3: Merge the Branch
Important: Always ask the user for confirmation before merging. Merging applies changes to the production model and cannot be easily undone.
curl -L -X POST "$OMNI_BASE_URL/api/v1/models/{modelId}/branch/{branchName}/merge" \
-H "Authorization: Bearer $OMNI_API_KEY"
If git with required PRs is configured, merge through your git workflow instead.
YAML File Types
| Type |
Extension |
Purpose |
| View |
.view |
Dimensions, measures, filters for a table |
| Topic |
.topic |
Joins views into a queryable unit |
| Relationships |
(special) |
Global join definitions |
Write with mode: "extension" (shared model layer). To delete a file, send empty yaml.
Writing Views
Basic View
dimensions:
order_id:
primary_key: true
status:
label: Order Status
created_at:
label: Created Date
measures:
count:
aggregate_type: count
total_revenue:
sql: ${sale_price}
aggregate_type: sum
format: currency_2
Dimension Parameters
See references/modelParameters.md for the complete list of 35+ dimension parameters, format values, and timeframes.
Most common parameters:
sql — SQL expression using ${field_name} references
label — display name · description — help text (also used by Blobby)
primary_key: true — unique key (critical for aggregations)
hidden: true — hides from picker, still usable in SQL
format — number2, currency2, percent_2, id
group_label — groups fields in the picker
synonyms — alternative names for AI matching (e.g., [client, account, buyer])
Measure Parameters
See references/modelParameters.md for the complete list of 24+ measure parameters and all 13 aggregate types.
Measure filters restrict rows before aggregation:
measures:
completed_orders:
aggregate_type: count
filters:
status:
is: complete
california_revenue:
sql: ${sale_price}
aggregate_type: sum
filters:
state:
is: California
Filter conditions: is, isnot, greaterthan, lessthan, contains, startswith, ends_with
Writing Topics
See Topics setup for complete YAML examples with joins, fields, and ai_context, and Topic parameters for all available options.
Key topic elements:
base_view — the primary view for this topic
joins — nested structure for join chains (e.g., users: {} or inventory_items: { products: {} })
aicontext — guides Blobby's field mapping (e.g., "Map 'revenue' → totalrevenue")
default_filters — applied to all queries unless removed
alwayswheresql — non-removable filters
fields — field curation: [orderitems.*, users.name, -users.internalid]
Writing Relationships
- join_from_view: order_items
join_to_view: users
on_sql: ${order_items.user_id} = ${users.id}
relationship_type: many_to_one
join_type: always_left
| Type |
When to Use |
manytoone |
Orders → Users |
onetomany |
Users → Orders |
onetoone |
Users → User Settings |
manytomany |
Tags ↔ Products (rare) |
Getting relationship_type right prevents fanout and symmetric aggregate errors.
Query Views
Virtual tables defined by a saved query:
schema: PUBLIC
query:
fields:
order_items.user_id: user_id
order_items.count: order_count
order_items.total_revenue: lifetime_value
base_view: order_items
topic: order_items
dimensions:
user_id:
primary_key: true
order_count: {}
lifetime_value:
format: currency_2
Or with raw SQL:
schema: PUBLIC
sql: |
SELECT user_id, COUNT(*) as order_count, SUM(sale_price) as lifetime_value
FROM order_items GROUP BY 1
Common Validation Errors
| Error |
Fix |
| "No view X" |
Check view name spelling |
| "No join path from X to Y" |
Add a relationship |
| "Duplicate field name" |
Remove duplicate or rename |
| "Invalid YAML syntax" |
Check indentation (2 spaces, no tabs) |
Docs Reference
Related Skills
- omni-model-explorer — understand the model before modifying
- omni-ai-optimizer — add AI context after building topics
- omni-query — test new fields