Data Model
Core objects
Understanding the few fundamental objects used by the Senso API will make the individual endpoints far easier to reason about.
Think of the model as three layers:
Below is a plain-English description of each object, when you would create it, and how it relates to the others.
1. Category
What it is
A high-level “bucket” that groups together broad areas of knowledge (e.g. “Mortgages”, “Compliance”, “Product Support”).
Why it exists
• Keeps very large knowledge bases navigable.
• Lets you scope searches or analytics to a business domain.
Key fields
Field | Type | Notes |
---|---|---|
category_id | UUID | Primary key |
name | string | Unique within your organisation |
description | string | Optional, up to 1 000 chars |
created_at | time | ISO-8601 |
2. Topic
What it is
A finer-grained subdivision that lives inside a category. Examples inside “Mortgages” might be “First-time buyers”, “Refinancing”, “Rates & fees”.
Why it exists
• Gives you more precise control when uploading or querying content.
• Lets UIs present hierarchies like Mortgages → Refinancing.
Relationship
Topic
has a many-to-one relationship with Category
(a topic belongs to exactly one category).
Key fields
Field | Type | Notes |
---|---|---|
topic_id | UUID | Primary key |
category_id | UUID | Parent category |
name | string | Unique within its category |
description | string | Optional |
created_at | time |
3. Content
A single piece of knowledge you want the system to understand and search.
There are three native flavours, identified by the type field:
raw
– plain text or markdown you POST directly.document
– a file you upload (PDF, DOCX, etc.).web
– (reserved) content pulled from a URL.
All content, regardless of type, goes through the same pipeline: ingestion → chunking → vectorisation. Once processing_status
reaches completed it is searchable and can be cited in generated answers.
Key fields
Field | Type/Example | Notes |
---|---|---|
id | UUID | Primary key |
type | enum | raw, document, web |
title | string | Human-readable title |
summary | string | Optional abstract |
version_num | int | Increments on each update |
processing_status | enum | queued → processing → completed/failed |
category_id | UUID | Optional for scoping |
topic_id | UUID | Optional for finer scope |
created_at | time |
For document content you’ll also see file_url
, file_name
, mime_type
once processing is complete.
For raw content you can fetch the full text
.
4. SearchResult (implicit)
When you call /search
, the API returns:
- the original
query
- an AI-generated
answer
- an array of results – each is a snippet (“chunk”) of content that backed the answer.
Fields inside each result let you trace back to the original content_id
, version_id
, and even chunk_index
for audit or debugging.
5. GenerateJob / Generated Content
The /generate
endpoint returns freshly composed text (generated_text
) along with the same style of sources
array used in search results.
If you set "save": true
in the request, the generated text is stored as a new content item and the response will include its content_id
.
How it all fits together
- Create Categories first, then Topics inside them.
- Upload Content (raw or files) and tag it with the relevant category/topic IDs—this is optional but highly recommended for precise search.
- Use /search to ask questions or /generate to have the model write something new, both of which cite underlying content chunks for transparency.
That’s the entire mental model: three persisted objects (Category, Topic, Content) plus two dynamic response types (SearchResult, Generated text). Everything else in the API is simply CRUD or query operations on these objects.