Skip to content

Data Model

Precis

This document defines every entity in Thinklio, the relationships between them, and the rules governing data ownership, visibility, and lifecycle. It is the single source of truth for "what things exist in this system and how they relate."

Thinklio's data model spans several functional domains. Core platform entities (accounts, teams, users) provide the multi-tenancy and membership substrate. Agent entities (agents, assignments, tools, templates) define what agents are, what they can do, and where they operate. Execution entities (interactions, steps, jobs, subjobs, observers) track all agent work from a single message through to multi-step deferred workflows. Core data structures (tasks, contacts, items, notes, tags) give agents structured objects to create, query, and update on behalf of users. Knowledge facts store structured information extracted from interactions, while the media and library system handles file storage, processing, and document-grounded retrieval. Communications entities manage outbound notification dispatch across channels. Platform services, billing, and access-control entities round out the model.

Every entity follows a consistent scoping model: user, team, or account. Visibility is governed by scope, with account policies acting as the ceiling that no lower layer can loosen. The type system for tasks, items, and notes provides extensibility: platform-reserved system types give consistent behaviour out of the box, while accounts can create custom types for domain-specific workflows.

The conceptual model described here is implementation-aware but not implementation-bound. The current implementation targets Convex (see doc 02 System Architecture); legacy references to Supabase Auth, PostgreSQL, and Redis reflect the original design and are retained where they provide useful context for migration. The Convex schema lives at /convex/schema.ts. Singular table names are used throughout per project convention.

For storage implementation details see doc 05 Persistence, Storage & Ingestion. For agent architecture and the execution contract see doc 03 Agent Architecture & Extensibility. For security and governance policies see doc 07 Security & Governance.

Table of contents

  1. Purpose
  2. Naming and type conventions
  3. Scope and visibility model
  4. Entity relationship overview
  5. Core platform entities
  6. Agent entities
  7. Agent execution entities
  8. Core data structures
  9. Knowledge
  10. Learned workflows
  11. Events
  12. Communications and channels
  13. Planning and execution scoring
  14. Platform services
  15. Media, storage and libraries
  16. Access and security
  17. Billing and resource management
  18. Agent tool integration
  19. API endpoints
  20. Agent usage patterns
  21. Data lifecycle
  22. Open questions
  23. Revision history

1. Purpose

This document serves three audiences:

Developers use it as the canonical reference for every table, field, relationship, and constraint in the system. When implementing a feature that touches data, this document answers "what exists, what are the rules, and where does it connect."

Architects use it to understand the full entity landscape, identify where new features land, and ensure consistency across the model.

Integration partners use the core data structures section (section 8), agent tool integration (section 18), and API endpoints (section 19) to build against a stable, published interface.

2. Naming and type conventions

Table names are singular throughout: account, agent, tool, task, note. Join tables use both singular names: account_user, team_member, agent_tool. Exception: user_profile instead of user (reserved word in PostgreSQL/Supabase).

Primary keys are UUIDs in the legacy Supabase model. In the Convex implementation, Convex's built-in document IDs serve the same purpose.

Timestamps use TIMESTAMPTZ in the legacy model. Convex stores timestamps as epoch milliseconds (v.number()).

Enums are expressed as string unions in Convex (v.union(v.literal(...), ...)). The conceptual model uses enum notation for clarity.

JSONB fields map to unvalidated or validated objects in Convex. Where the schema is known, Convex validators enforce structure at write time.

Embeddings use vector(1536) in the legacy model. Convex vector search uses the built-in vector index mechanism.

3. Scope and visibility model

All scoped entities follow the platform's standard three-tier model:

Scope Visible to Typical use
user The owning user only Personal tasks, private contacts, personal notes
team All members of the team Shared task lists, team contacts, team items
account All account members Organisation-wide items, shared contact database, company notes

Exceptions and refinements:

  • Items are never user-scoped. They are organisational by nature; the minimum scope is team.
  • Contacts carry an additional visibility field (private, team, account) that can restrict access below the scope level. A private contact in an account scope is visible only to its creator.
  • Admins and owners can see all entities regardless of visibility, for administration and data management purposes.
  • Notes can be shared beyond their scope via note_share records without changing the note's scope.
  • RLS policies (in the legacy Supabase model) enforce scope membership via account_user and team_member. In Convex, equivalent enforcement runs in middleware (see doc 07).

Policy layering: Account policies set the ceiling. Team policies can tighten within account bounds. User preferences can tighten further. No layer can loosen a restriction set above it. This is consistent across the entire platform, not just the data model.

4. Entity relationship overview

┌──────────────┐
│   account    │
└──────┬───────┘
       │ has many
       ├──────────────┐
       │              │
┌──────▼───────┐ ┌────▼──────────┐
│     team     │ │  account_user │ (membership + role)
└──────┬───────┘ └───────────────┘
       │ has many
┌──────▼───────┐
│ team_member  │ (membership + role)
└──────────────┘

┌──────────────┐
│    agent     │ (first-class, independent)
└──────┬───────┘
       │ assigned via
┌──────▼────────────────┐
│ agent_assignment      │ (agent → user | team | account, with scope
│                       │  and per-assignment tool restrictions)
└───────────────────────┘

┌──────────────┐
│ user_profile │ (platform-global identity)
└──────────────┘

┌──────────────┐        ┌──────────────┐
│ interaction  │───────▶│     step     │ (1:many)
│              │        └──────────────┘
│              │
│              │───────▶ parent_interaction_id (delegation chain)
└──────────────┘

┌──────────────┐        ┌──────────────┐
│     job      │───────▶│    subjob    │ (1:many)
│              │        └──────────────┘
│              │
│              │───────▶┌──────────────┐
│              │        │ job_observer │ (many observers per job)
└──────────────┘        └──────────────┘

┌──────────────────────────────────────┐
│      Core Data Structures            │
├──────────┐  ┌──────────┐  ┌────────┐│
│   task   │  │ contact  │  │  item  ││
└────┬─────┘  └────┬─────┘  └───┬────┘│
     │             │             │     │
     └─────────────┼─────────────┘     │
                   ▼                   │
                 note                  │
                   │                   │
                   └──────▶ tag ◀──────┘
                        entity_tag
                   (task/contact/item/note)
└──────────────────────────────────────┘

┌──────────────┐
│knowledge_fact│ (scoped: agent | account | team | user)
└──────────────┘

┌──────────────┐
│    tool      │ (type: internal | external | mcp | agent)
└──────┬───────┘
       │ assigned via
┌──────▼───────┐
│  agent_tool  │ (agent → tool, with permission level)
└──────────────┘

┌──────────────┐
│   workflow   │ (learned, first-class)
└──────────────┘

┌──────────────┐
│    event     │ (immutable, append-only)
└──────────────┘

Additional entity groups (abbreviated in the diagram for clarity): platform services (platform_service, llm_model, account_service_config, account_llm_preference), communications (channel_config, user_channel, user_comm, notification), planning (canonical_plan, execution_outcome, plan_score), media and storage (storage_bucket, account_storage_bucket, media, media_processor, media_processing_rule, media_processing_job, library, library_item, agent_library), access and security (invitation, api_key, oauth_token, webhook_subscription), and billing (credit_ledger, budget_limit, usage_record, quality_rating, platform_config).

5. Core platform entities

5.1 user_profile

A person on the platform. Users have a single global identity and can participate in multiple accounts and teams. In the legacy model, authentication is managed by Supabase Auth and user_profile is synced from auth.users via a database trigger on signup. In the Convex model, authentication is managed by Clerk; the user_profile record is created on first login via a webhook.

Field Type Description
id UUID Primary key (synced from auth provider)
display_name string How the user appears in the platform
status enum active, suspended, deleted
settings JSONB Personal preferences and configuration
created_at timestamp Account creation
updated_at timestamp Last modification

Rules:

  • A user exists independently of any account.
  • A user can create agents directly for personal use.
  • A user can belong to multiple accounts with different roles.
  • Deleting a user triggers data portability and retention procedures (section 21).
  • Credentials are never stored in this table.

5.2 account

A billing and governance entity. Accounts own policies, budgets, and administrative control. In the Convex model, accounts map to Clerk organisations.

Field Type Description
id UUID Primary key
name string Display name
slug string URL-safe identifier, unique
plan enum free, team, account, enterprise
settings JSONB Account-wide settings and preferences
policies JSONB Governance policies (tool restrictions, content rules, delegation limits)
budget_monthly decimal Monthly spending limit
status enum active, suspended, archived
created_at timestamp
updated_at timestamp

Rules:

  • An account is the anchor for billing.
  • Account policies override all lower-level settings.
  • Budget enforcement happens at the account level first, then team, then user.
  • Account policies include delegation governance: max_delegation_depth, delegation approval requirements.

5.3 account_user

Links users to accounts with roles.

Field Type Description
account_id UUID FK to account
user_id UUID FK to user_profile
role enum owner, admin, editor, viewer
joined_at timestamp When the user joined

Role descriptions:

  • owner: Full access including billing, account deletion, and ownership transfer.
  • admin: Manage teams, members, agents, and settings. No billing or account deletion.
  • editor: Create and edit agents, manage knowledge, use all agent features.
  • viewer: Read-only access. Can interact with assigned agents but cannot create or modify configuration.

Rules:

  • Every account has at least one owner.
  • Owners can manage admins and members; admins can manage editors and viewers.
  • A user's role determines what they can configure within the account.

5.4 team

A group of users within an account who share context and agents.

Field Type Description
id UUID Primary key
account_id UUID FK to account (nullable for personal teams)
name string Display name
slug string URL-safe, unique within account
settings JSONB Team-specific settings
budget_monthly decimal Team spending limit (within account budget)
status enum active, archived
created_at timestamp
updated_at timestamp

Rules:

  • Teams belong to accounts, or are standalone for personal use.
  • Team knowledge is isolated: Team A's knowledge is invisible to Team B.
  • Team budgets must not exceed the parent account budget.

5.5 team_member

Links users to teams with roles.

Field Type Description
team_id UUID FK to team
user_id UUID FK to user_profile
role enum admin, member, readonly
joined_at timestamp When the user joined

6. Agent entities

6.1 agent

A first-class platform entity: the AI assistant. Agents exist independently and are assigned to contexts. For the full agent architecture, execution contract, and composition model see doc 03.

Field Type Description
id UUID Primary key
name string Display name
slug string URL-safe identifier
description string What this agent does
system_prompt text Core personality and instructions
capability_level enum tools_only, workflow, experimental, learning
llm_provider string Which LLM provider to use
llm_model string Which model to use
settings JSONB Agent-specific configuration
status enum active, paused, archived
created_by UUID FK to user_profile
template_id UUID FK to agent_catalog (nullable)
created_at timestamp
updated_at timestamp

Rules:

  • Agents are created by users (personally) or by account/team admins.
  • An agent can be assigned to multiple contexts simultaneously.
  • Agent knowledge (domain expertise, learned workflows) travels with the agent.
  • The capability_level determines what the agent is allowed to do.
  • Pausing stops new interactions; archiving preserves the audit trail.
  • An agent can be registered as a tool (type agent) for other agents to invoke via delegation.

6.2 agent_assignment

Links agents to contexts (users, teams, or accounts) with scope configuration and per-assignment tool restrictions.

Field Type Description
id UUID Primary key
agent_id UUID FK to agent
context_type enum user, team, account
context_id UUID FK to user_profile, team, or account
access_level enum interact, configure, admin
knowledge_scope JSONB Which knowledge layers are active
budget_monthly decimal Budget limit for this assignment
tool_restrictions JSONB Per-tool permission overrides that narrow the agent's configured capabilities
status enum active, suspended
created_at timestamp
updated_at timestamp

Rules:

  • An agent can have multiple assignments (to a team and to individual users, for example).
  • Each assignment has its own knowledge scope and budget.
  • Knowledge isolation is per-assignment: Team A's knowledge is separate from Team B's, even for the same agent.
  • Removing an assignment does not delete the agent.
  • tool_restrictions can only narrow, never widen, the agent's configured tool permissions. An assignment cannot grant tool access that the agent does not already have.
  • Tool restrictions apply equally to regular tools and agent-as-tool delegations. A scheduler agent assigned to a team can be restricted to read-only calendar operations for that specific assignment.

Tool restrictions format:

{
    "scheduler_agent": {
        "allowed_actions": ["find_free_time", "check_conflicts"],
        "denied_actions": ["create_event"]
    },
    "search_web": {
        "max_calls_per_interaction": 3
    },
    "send_email": {
        "blocked": true
    }
}

Policy evaluation order for tool access:

  1. Agent configuration: does the agent have this tool assigned? (Maximum capability.)
  2. Assignment restrictions: does the assignment narrow the tool's permissions for this context?
  3. Account policies: do account-level policies impose further restrictions?

Each layer can only restrict, never expand.

6.3 tool

A capability available to agents. Tools can be platform-provided, external webhook/API integrations, MCP-discovered, or other agents (agent-as-tool delegation).

Field Type Description
id UUID Primary key
slug string Unique identifier
name string Display name
description text What the tool does (used in LLM tool definitions)
type enum internal, external, mcp, agent
trust_level enum read, low_risk_write, high_risk_write
parameter_schema JSONB JSON Schema for parameters (also serves as the invocation contract for agent-type tools)
return_schema JSONB JSON Schema for return values
config JSONB Endpoint URL, auth, timeout for external tools. Agent ID for agent-type tools. MCP server reference for MCP tools.
rate_limit JSONB Rate limiting configuration
default_execution_mode enum immediate, deferred (default: immediate)
version string Tool version
status enum active, deprecated, disabled
registered_by UUID FK to user_profile or account (nullable for platform tools)
created_at timestamp
updated_at timestamp

Rules for agent-type tools:

  • When type is agent, the config field contains {"agent_id": "uuid"} identifying the delegate agent.
  • The parameter_schema defines the invocation contract: what structured input the delegate accepts.
  • The return_schema defines what structured output the delegate produces.
  • The trust_level reflects the delegate agent's most sensitive configured capability.
  • Cycle detection is enforced at configuration time (when registering an agent-as-tool, the system checks for cycles in the delegation graph) and at runtime (each delegation carries a chain of agent IDs; invoking an agent already in the chain is denied).

Rules for dynamic registration (Integration API):

  • External systems can register tools through the Integration API with governance approval.
  • Dynamically registered tools go through the same policy evaluation as statically configured tools.
  • The registered_by field tracks who registered the tool for audit purposes.
  • Health monitoring applies to external and dynamically registered tools; unhealthy tools are circuit-broken.

6.4 agent_tool

Links agents to tools with permission levels.

Field Type Description
agent_id UUID FK to agent
tool_id UUID FK to tool
permission enum read, readwrite
config_override JSONB Agent-specific tool configuration
created_at timestamp

6.5 agent_catalog

Pre-configured templates for creating agents, including delegation relationships for composed agents. In doc 03 this entity is referred to as the platform catalogue; in the legacy model it was agent_template.

Field Type Description
id UUID Primary key
name string Template name
description text What kind of agent this creates
system_prompt text Default system prompt
capability_level enum Default capability level
tool_assignments JSONB Default tool assignments (including agent-as-tool delegations)
delegation_config JSONB Pre-configured delegation relationships and per-delegation restrictions
knowledge_seeds JSONB Initial knowledge facts
library_assignments JSONB Array of {library_id, priority} objects; when an agent is deployed these become agent_library records
scope enum platform, account
scope_id UUID FK to account (for account-specific templates)
created_by UUID FK to user_profile
created_at timestamp

Rules:

  • Templates for composed agents (Personal Assistant with Scheduler and Research, for example) pre-configure delegation relationships and tool assignments.
  • Cycle detection runs when a template is created or modified that includes agent-as-tool delegations.
  • Platform templates (scope = platform) are available to all accounts. Account templates are scoped to a single account.
  • Platform templates may reference platform-scoped libraries; account templates may reference account-scoped libraries within their scope.

7. Agent execution entities

7.1 interaction

A unit of work initiated by a user message. Contains one or more steps. Supports delegation chains through parent interaction linking. For the full execution contract and harness design see doc 03.

Field Type Description
id UUID Primary key
agent_id UUID FK to agent
user_id UUID FK to user_profile
team_id UUID FK to team (nullable)
account_id UUID FK to account (nullable)
session_id UUID Groups interactions into conversations
channel string Which channel initiated this interaction
state enum pending, running, success, failed
total_cost decimal Sum of all step costs
parent_interaction_id UUID FK to interaction (nullable). Set when this interaction is a delegation from another agent's act step.
delegation_depth int Current depth in the delegation chain. 0 for top-level interactions.
started_at timestamp When the interaction began
completed_at timestamp When the interaction finished
metadata JSONB Trace ID, channel-specific data

Rules:

  • State is derived from constituent steps (see harness design in doc 03).
  • Cost is the sum of all step costs.
  • The account_id and team_id provide cost attribution context.
  • When parent_interaction_id is set, this interaction's cost becomes the act step cost in the parent interaction. Cost rolls up through the delegation chain to the originating user, team, and account.
  • Delegation depth is checked against the account policy's max_delegation_depth setting (default: 3). The policy engine denies delegations that would exceed this limit.

7.2 step

An individual unit of execution within an interaction. Steps carry an execution mode that determines how the harness orchestrates post-step flow.

Field Type Description
id UUID Primary key
interaction_id UUID FK to interaction
step_type enum context, think, act, observe, respond, extract
step_order int Sequence within the interaction
state enum created, running, success, failed
execution_mode enum immediate, deferred, interactive (nullable, only relevant for act steps)
input_data JSONB What went into this step
output_data JSONB What came out of this step
error_data JSONB Failure reason and details (if failed)
cost decimal Cost of this step
cost_detail JSONB Breakdown: tokens in, tokens out, API costs
job_id UUID FK to job (nullable). Set when a deferred act step creates a job.
started_at timestamp When execution began
completed_at timestamp When execution finished

Rules:

  • Steps follow the state machine: created then running then success or failed.
  • Failed steps include error_data with reason: timeout, error, governance, budget, cancellation, delegation_depth_exceeded, delegation_cycle_detected.
  • Steps are persisted before execution begins (created state). Results are persisted on completion.
  • Resumption: find steps in created or running state and re-execute.
  • For act steps, execution_mode determines harness behaviour:
  • immediate: step executes synchronously, harness waits for result.
  • deferred: step dispatches work and creates a Job, succeeds on dispatch (not on work completion).
  • interactive: step result feeds back into a new think step for further reasoning.
  • A deferred act step's job_id links to the job entity for tracking.

7.3 job

A unit of work that outlives a single interaction. Created when a deferred act step dispatches work to an external execution engine or a delegate agent with unpredictable completion time. For the full job system design see doc 03 section 8.

Field Type Description
id UUID Primary key
type string Descriptor: research, handoff, workflow, delegation, etc.
created_by_agent UUID FK to agent that created this job
created_by_interaction UUID FK to interaction that spawned this job
session_id UUID Conversational context for follow-up interactions
state enum pending, dispatched, in_progress, resolved, failed, cancelled, timed_out
has_useful_output boolean Whether partial output is available and worth notifying about
dispatch_target JSONB Webhook URL, agent ID, external system config
dispatch_payload JSONB Structured brief/parameters for the executor
context_bundle JSONB Accumulated state from prior steps, carried forward for follow-up interactions
usefulness_rule string Rule identifier (default: any_completed_subjob)
timeout_at timestamp Deadline for the timeout monitor
created_at timestamp
updated_at timestamp Last state change

State machine:

pending -> dispatched -> in_progress -> resolved
                                     -> failed
                                     -> cancelled
                                     -> timed_out

Rules:

  • Jobs inherit the governance context (budget, policies, audit trail) of the creating interaction. Deferred execution is not an escape hatch from governance.
  • Active jobs (non-terminal states) live in Redis for fast reads and writes in the legacy model. Terminal jobs are flushed asynchronously to PostgreSQL for durable storage and audit. In the Convex model, all job state lives in the Convex database with real-time subscriptions replacing polling.
  • The context_bundle carries forward state that follow-up interactions need. When a PA checks a calendar (immediate step) then dispatches research (deferred step), the calendar result is stored in the job's context_bundle so the follow-up interaction has access to both the research output and the earlier calendar result.
  • resolved means all subjobs have reached a terminal state and at least one succeeded. The observing agent evaluates whether the result set constitutes full success, acceptable partial success, or effective failure.
  • failed means all subjobs terminated and none succeeded.
  • Retention follows the same policies as interactions and events (configurable per account, default 90 days active, then archived).

7.4 subjob

A discrete unit of work within a job. A simple deferred job has a single subjob. A complex job (generate five research articles, for example) has multiple subjobs, enabling granular progress tracking and partial output notification.

Field Type Description
id UUID Primary key
job_id UUID FK to job
label string Human-readable description
order int Sequence within the job
state enum pending, running, completed, failed
result_data JSONB Output from this subjob
error_data JSONB Failure details (if failed)
started_at timestamp When execution began
completed_at timestamp When execution finished

Rules:

  • When a subjob completes, the parent job evaluates its usefulness rule.
  • The default rule (any_completed_subjob): at least one subjob completed with non-null result_data while other subjobs are still in progress sets has_useful_output = true on the job and notifies qualifying observers.
  • When all subjobs reach terminal states: the job transitions to resolved (if any succeeded) or failed (if none succeeded).
  • Subjobs within a timed-out or cancelled job are transitioned to failed with appropriate error reasons.

7.5 job_observer

An entity registered to receive notifications about a job's state changes. Decouples job creation from job consumption.

Field Type Description
job_id UUID FK to job
observer_type enum agent, system
observer_id UUID Agent ID or system process identifier
assignment_id UUID Which assignment context governs this observer's permissions
notify_on enum completion_only, failure_only, partial_and_completion, all_changes
callback_metadata JSONB Context the observer needs when notified
registered_at timestamp When the observer was registered

Notification filtering:

notify_on Receives
completion_only Terminal states only: resolved, failed, cancelled, timed_out
failure_only Failed, cancelled, timed_out only
partial_and_completion Terminal states plus partial output availability
all_changes Every state transition

Rules:

  • The creating agent is automatically registered as an observer when a job is created.
  • Additional observers can be added by any agent with visibility into the job (governed by assignment context and account policies).
  • Terminal state notifications bypass observer preferences: all observers are notified when a job reaches a terminal state.
  • A periodic cleanup process removes observer registrations pointing to deleted agents.
  • Observer types include agent (internal) and system (for monitoring processes and, in future, external webhook subscribers via the Platform API).

8. Core data structures

These four universal data structures give agents structured objects to work with. They are not app features; they are the substrate that agents create, query, and update on behalf of users. The UI surfaces them, but the primary interface is the agent tool system (section 18).

Design principles:

  1. Agent-first. These structures exist so agents can operate on structured data. The UI is a secondary read/write interface.
  2. Scope-aware. Every entity follows the standard scoping model (section 3). Visibility is governed by scope and the role model.
  3. Lightweight. These are not replacements for Jira, Salesforce, or Notion. They provide just enough structure for agents to work with. Accounts that need deeper functionality use integrations (Todoist, HubSpot) with optional two-way sync.
  4. Extensible via metadata. Every entity carries a metadata JSONB field for domain-specific data without schema changes. A support agent stores SLA data in item metadata. A CRM agent stores deal stage in contact metadata.
  5. Published API. The tool and REST interfaces are public. Custom agents and external integrations use these structures exactly as built-in agents do.
  6. Typed and customisable. Tasks, items, and notes have a type system: platform-reserved system types provide consistent behaviour, while accounts can create custom types for domain-specific workflows.

8.1 task_list

A named group of tasks. Lists provide simple organisation without the overhead of projects or boards.

Field Type Description
id UUID PK
account_id UUID FK to account
scope text user, team, account
scope_id UUID Scope entity ID
name text Display name (e.g. "Shopping", "Q2 Goals", "Sprint 14")
description text Optional
position integer Ordering among lists in the same scope
is_archived boolean Soft-delete for completed lists
created_by UUID FK to user_profile
created_at timestamp
updated_at timestamp

Visibility: User-scoped lists are private. Team-scoped lists are visible to team members. Account-scoped lists are visible to all account members.

8.2 task

An individual unit of work. Can belong to a list or be unassigned (inbox).

Field Type Description
id UUID PK
account_id UUID FK to account
list_id UUID FK to task_list (nullable for unassigned tasks)
task_type_id UUID FK to task_type (nullable, defaults to standard task)
title text
description text Optional, supports markdown
status text todo, in_progress, done, cancelled
priority text urgent, high, normal, low
due_date date Optional
due_time time Optional, for time-specific deadlines
assigned_to UUID FK to user_profile (nullable)
created_by UUID FK to user_profile (nullable for agent-created)
created_by_agent UUID FK to agent (nullable for user-created)
completed_at timestamp Set when status transitions to done
position integer Ordering within list
scope text user, team, account
scope_id UUID
source_item_id UUID FK to item (nullable, if task was generated from a ticket/request)
recurrence text RRULE string for recurring tasks (nullable). RFC 5545 format, e.g. FREQ=WEEKLY;BYDAY=MO
recurrence_parent_id UUID FK to task (self-referencing, nullable). Points to the original recurring task definition.
next_occurrence timestamp Next scheduled occurrence for the parent recurring task (nullable)
metadata JSONB Domain-specific fields
created_at timestamp
updated_at timestamp

Key indexes:

  • (account_id, assigned_to, status, due_date) for "what's due for this user?"
  • (account_id, list_id, position) for ordered list view
  • (account_id, scope, scope_id, status) for scoped task queries
  • (account_id, recurrence_parent_id) for finding generated occurrences of a recurring task
  • (next_occurrence) where recurrence IS NOT NULL for the recurring task scheduler

Relationship to items: When an item (ticket/request) generates work for a user, a task is created with source_item_id linking back. The item tracks the request lifecycle; the task tracks the user's work. Completing the task may or may not resolve the item.

Recurring tasks: A task with a recurrence RRULE is a recurring task definition. The platform generates individual task instances as children (linked via recurrence_parent_id) when each occurrence is due. The parent task's next_occurrence is updated after each generation. Completing or cancelling a child task does not affect the parent or future occurrences. Modifying or deleting the parent stops future generation.

8.3 task_type

Categorises tasks into system-reserved and account-custom types.

Field Type Description
id UUID PK
account_id UUID FK to account (nullable: NULL for system types)
slug text Machine-readable identifier, e.g. standard, reminder, follow_up
name text Display name
description text Optional
is_system boolean true for platform-reserved types, false for account-created
icon text Optional icon identifier for UI
default_status text Default initial status for tasks of this type
metadata JSONB Type-specific configuration (e.g. allowed statuses, default priority)
created_at timestamp

System-reserved vs account-custom pattern: System types (is_system = true, account_id = NULL) are seeded by the platform and available to all accounts. They cannot be modified or deleted. Account-custom types (is_system = false, account_id set) are created by account admins for domain-specific workflows. System types include: standard, reminder, follow_up, onboarding, review, recurring.

Unique constraint: (account_id, slug). No duplicate slugs per account. System types use (NULL, slug).

8.4 contact

A person or organisation that the account interacts with.

Field Type Description
id UUID PK
account_id UUID FK to account
type text person, organisation
name text Full name or organisation name
email text Primary email (nullable)
phone text Primary phone (nullable)
title text Job title or role, persons only (nullable)
website text (nullable)
organisation_id UUID FK to contact (self-referencing, for "person works at org")
visibility text private, team, account
scope text user, team, account
scope_id UUID
source text manual, agent, import, integration
source_ref text External ID if imported (e.g. HubSpot contact ID)
metadata JSONB Flexible fields: address, social profiles, custom fields, deal info
created_by UUID FK to user_profile
created_by_agent UUID FK to agent
created_at timestamp
updated_at timestamp

Visibility model:

  • private: only the creating user can see this contact. Personal contacts (friends, family, personal network).
  • team: visible to all members of the contact's team scope. Team-level business contacts.
  • account: visible to all account members. Shared business contacts, customers, partners.

Admins and owners can see all contacts regardless of visibility (for data management purposes).

Organisation linking: A person contact can reference an organisation contact via organisation_id. This enables "show me everyone at Acme Corp" queries without a separate join table.

Integration sync: When an account connects HubSpot or another CRM, contacts can be synced bidirectionally. source = 'integration' and source_ref tracks the external ID to prevent duplicates and enable sync.

8.5 contact_interaction

A lightweight log of interactions with a contact. Not a full activity stream, just enough for agents to answer "when did we last talk to this person?"

Field Type Description
id UUID PK
contact_id UUID FK to contact
type text call, email, meeting, note, message, other
summary text One-line summary
detail text Optional longer description
occurred_at timestamp When the interaction happened
logged_by UUID FK to user_profile (nullable)
logged_by_agent UUID FK to agent (nullable)
metadata JSONB Duration, channel, linked email ID, etc.
created_at timestamp

8.6 item

An externally-initiated thing that needs processing. Items differ from tasks: tasks are "I need to do X", items are "X arrived and needs handling."

Field Type Description
id UUID PK
account_id UUID FK to account
item_type_id UUID FK to item_type (nullable, defaults to generic item)
type text support_ticket, request, bug, inquiry, approval, other
title text
description text Supports markdown
status text open, in_progress, waiting, resolved, closed
priority text urgent, high, normal, low
source text email, web, api, agent, manual, channel
source_ref text External reference (email message ID, form submission ID, etc.)
assigned_to_user UUID FK to user_profile (nullable)
assigned_to_team UUID FK to team (nullable)
assigned_to_agent UUID FK to agent (nullable, for auto-processing)
contact_id UUID FK to contact (who submitted, nullable)
resolved_at timestamp
closed_at timestamp
scope text team, account (items are never user-scoped)
scope_id UUID
metadata JSONB SLA data, form fields, channel context, etc.
created_by UUID FK to user_profile (nullable)
created_by_agent UUID FK to agent (nullable)
created_at timestamp
updated_at timestamp

Key indexes:

  • (account_id, status, priority) for queue management
  • (account_id, assigned_to_user) for "my items"
  • (account_id, contact_id) for "items from this contact"
  • (account_id, type, status) for filtered views

Item to task relationship: When an item is assigned to a user, the assigning agent (or admin) may create a task linked via task.source_item_id. The item tracks the external request lifecycle. The task tracks the internal work. An item may generate zero, one, or many tasks.

Item workflow:

open -> in_progress -> resolved -> closed
         |                ^
       waiting -----------+

waiting = blocked on external input (waiting for customer reply, for example). resolved = work complete, pending confirmation. closed = confirmed done (or auto-closed after timeout).

8.7 item_type

Categorises items into system-reserved and account-custom types. Same pattern as task_type.

Field Type Description
id UUID PK
account_id UUID FK to account (nullable: NULL for system types)
slug text Machine-readable identifier, e.g. support_ticket, request, bug
name text Display name
description text Optional
is_system boolean true for platform-reserved types
icon text Optional icon identifier
default_status text Default initial status
default_priority text Default priority
allowed_statuses JSONB Array of valid statuses for this type (nullable, uses default workflow if not set)
metadata JSONB Type-specific configuration
created_at timestamp

System types include: support_ticket, request, bug, inquiry, approval, other. Accounts can create custom types for domain-specific workflows (e.g. rfp, compliance_review, warranty_claim).

8.8 note

Freeform captured information. Meeting summaries, research, observations, ideas. Stored in markdown format.

Field Type Description
id UUID PK
account_id UUID FK to account
note_type_id UUID FK to note_type (nullable, defaults to general note)
title text Optional
content text Markdown-format freeform text
scope text user, team, account
scope_id UUID
linked_type text task, contact, item, agent (nullable)
linked_id UUID FK to linked entity (nullable)
created_by UUID FK to user_profile (nullable)
created_by_agent UUID FK to agent (nullable)
metadata JSONB
created_at timestamp
updated_at timestamp
edited_at timestamp Set when content is modified after creation (nullable). Distinct from updated_at which changes on any field update. Enables "edited" indicators in the UI.

Linking: A note can be linked to one primary entity. "Notes from call with John" uses linked_type = 'contact'. "Research for task X" uses linked_type = 'task'. Notes without links are standalone (ideas, observations).

Knowledge extraction: Notes are candidates for automatic knowledge fact extraction. When a note is created, the platform can optionally run the fact_extract processor to derive structured knowledge facts from the freeform content.

Storage format: Note content is stored as markdown. The API accepts and returns markdown. Client rendering is the responsibility of the UI layer.

8.9 note_share

Granular sharing of individual notes beyond their scope visibility.

Field Type Description
id UUID PK
note_id UUID FK to note
shared_with_user UUID FK to user_profile (nullable, one of user/team must be set)
shared_with_team UUID FK to team (nullable)
permission text view, edit
shared_by UUID FK to user_profile
created_at timestamp

Purpose: Allows a user-scoped note to be shared with specific users or teams without changing its scope. A user can share their private meeting notes with a colleague without making them team-visible. The note's scope remains user but the note_share records grant additional access.

8.10 note_mention

Tracks @mentions within notes for cross-entity linking and notification.

Field Type Description
id UUID PK
note_id UUID FK to note
mentioned_type text user, contact, task, item, agent
mentioned_id UUID ID of the mentioned entity
position integer Character offset in the note content (for UI highlighting)
created_at timestamp

Purpose: When a note contains @John or @Task:Fix login bug, the platform extracts mentions and stores them. This enables notifications to mentioned users, "notes mentioning me" queries, cross-entity discovery ("find all notes that mention this contact"), and UI autocomplete for @mentions.

8.11 note_type

Categorises notes into system-reserved and account-custom types. Same pattern as task_type and item_type.

Field Type Description
id UUID PK
account_id UUID FK to account (nullable: NULL for system types)
slug text Machine-readable identifier, e.g. general, meeting, research
name text Display name
description text Optional
is_system boolean true for platform-reserved types
icon text Optional icon identifier
metadata JSONB Type-specific configuration
created_at timestamp

System types include: general, meeting, research, decision, standup, retrospective, idea. Accounts can create custom types for domain-specific note categories.

8.12 tag

Cross-cutting categorisation for any entity.

Field Type Description
id UUID PK
account_id UUID FK to account
name text Display name (e.g. "Urgent", "VIP", "Q2", "Follow-up")
colour text Hex colour for UI display
created_at timestamp

Unique constraint: (account_id, name). No duplicate tag names per account.

8.13 entity_tag

Polymorphic join table linking tags to any entity.

Field Type Description
id UUID PK
tag_id UUID FK to tag
entity_type text task, contact, item, note
entity_id UUID
created_at timestamp

Unique constraint: (tag_id, entity_type, entity_id). No duplicate tag assignments.

8.14 Entity attachments

Media files (images, documents, etc.) can be attached to any core data entity using the media table's polymorphic linking fields (linked_type and linked_id). This uses the existing media entity (section 15) rather than a separate attachment table. When a user attaches a file to a task or note, a media record is created with linked_type and linked_id pointing to the entity. Querying attachments is a simple filter on media by those two fields.

9. Knowledge

9.1 knowledge_fact

A structured piece of knowledge with scope and ownership. For the four-layer knowledge architecture (agent, account, team, user) and runtime resolution see doc 03 section 11.

Field Type Description
id UUID PK
scope enum agent, account, team, user
scope_id UUID FK to agent, account, team, or user_profile
agent_id UUID FK to agent (which agent this knowledge is for)
subject string What the fact is about
predicate string The relationship or attribute
value text The fact content
category string Classification: identity, preference, project, procedure, etc.
confidence float Extraction confidence (0.0 to 1.0)
source_interaction_id UUID FK to interaction that produced this fact
embedding vector(1536) Semantic embedding for similarity search
access_count int How often this fact has been retrieved
last_accessed timestamp Last retrieval time
created_at timestamp
updated_at timestamp

Rules:

  • scope and scope_id together determine visibility:
  • agent scope: visible whenever this agent is used.
  • account scope: visible to all account members using this agent.
  • team scope: visible to team members only.
  • user scope: visible only to that specific user.
  • Precedence for conflicts: account > agent > team > user.
  • User facts are portable: they follow the user across accounts.
  • Team facts stay with the team when a user leaves.
  • When an agent delegates to another agent, knowledge extracted during the delegate's interaction is scoped to the delegate agent's assignment context. The invoking agent does not automatically receive knowledge extracted by the delegate. Cross-agent knowledge sharing happens through normal interaction flow.

10. Learned workflows

10.1 workflow

A codified problem-solving pattern learned by an agent. Distinct from the Convex Workflow component (which provides durable execution infrastructure); this entity captures reusable reasoning patterns.

Field Type Description
id UUID PK
agent_id UUID FK to agent that learned this workflow
name string Human-readable name
description text What problem this workflow solves
trigger_pattern text What kind of request triggers this workflow
steps JSONB Ordered list of step definitions
scope enum agent, team, account
scope_id UUID FK to scope entity (for promoted workflows)
success_count int Times used successfully
version int Workflow version
status enum draft, active, approved, deprecated
created_at timestamp
updated_at timestamp

Rules:

  • Workflows start in agent scope (private to the agent).
  • Admins can promote workflows to team or account scope.
  • Promotion creates a copy; the original remains with the agent.
  • Workflows require approval before becoming active at team or account level.

11. Events

11.1 event

The append-only event store. For the event system design and harness integration see doc 06.

Field Type Description
id UUID PK
type string Event type (e.g. message.received, job.state_changed)
source string Originating service
agent_id UUID FK to agent
user_id UUID FK to user_profile (nullable)
team_id UUID FK to team (nullable)
account_id UUID FK to account (nullable)
session_id UUID Session context
parent_id UUID Parent event for chains
direction enum inbound, outbound, internal
content text Event content (message text, etc.)
payload JSONB Type-specific structured data
metadata JSONB Trace ID, version, priority
created_at timestamp Event timestamp

Rules:

  • Events are immutable: never updated or deleted.
  • Partitioned by created_at (monthly) for performance in the legacy model.
  • The system of record: all state can be derived from events.
  • Subject to data retention policies (archival after configurable period).
  • Job-related events (job.created, job.dispatched, job.state_changed, job.resolved, job.failed, job.cancelled, job.timed_out) follow the same immutability and retention rules.

12. Communications and channels

12.1 channel_config

Channel type configurations. For the full channel architecture see doc 06.

Field Type Description
id UUID PK
account_id UUID FK to account
channel_type enum telegram, email, web, api
config JSONB Configuration (encrypted bot tokens, API keys, etc.)
status enum active, disabled
created_at timestamp
updated_at timestamp

Rules:

  • Credentials in config are encrypted in the secrets vault; only references are stored here.
  • Each account can have multiple channels of the same type (multiple email accounts, for example).

12.2 user_channel

User-to-channel identity links. Connects a user's external identity (Telegram ID, email address) to their Thinklio account so messages from any linked channel are attributed to the correct user.

Field Type Description
id UUID PK
user_id UUID FK to user_profile
channel_type string Channel identifier (e.g. telegram, email)
external_id string User's ID in the external system (Telegram user ID, email address)
display_name string How the user appears in that channel
status enum pending_verification, active, disabled
verified_at timestamp When verification completed
last_active_at timestamp Last message or activity
created_at timestamp

12.3 user_comm

Outbound notification dispatch queue. Handles messages that the platform needs to deliver to users outside of agent conversations.

Field Type Description
id UUID PK
account_id UUID FK to account
user_id UUID FK to user_profile
comm_type text task_reminder, task_due, item_assigned, mention, share, digest, system
channel text Preferred delivery channel: email, push, in_app, telegram
subject text Message subject/title
body text Message content (markdown)
entity_type text Related entity type (nullable): task, item, note, etc.
entity_id UUID Related entity ID (nullable)
status text pending, sent, delivered, failed, cancelled
scheduled_for timestamp When to send (nullable: NULL = send immediately)
sent_at timestamp When actually sent (nullable)
error text Error message if delivery failed (nullable)
metadata JSONB Channel-specific data, retry count, etc.
created_at timestamp

Dispatch pattern: A background worker polls user_comm for pending messages, resolves the user's preferred delivery channel, and dispatches via the appropriate transport (email via Postmark, push notification, in-app notification record, or Telegram message). Failed deliveries are retried with exponential backoff. A reminders module generates task reminder and due-date notifications by scanning tasks and creating user_comm records.

Relationship to notifications: The notification table stores in-app notifications (visible in the UI notification centre). user_comm is the dispatch queue for all outbound channels. When channel = 'in_app', the worker creates a notification record. For other channels, it dispatches externally.

12.4 notification

In-app notification records, visible in the UI notification centre.

Field Type Description
id UUID PK
user_id UUID FK to user_profile
account_id UUID FK to account
title string Notification title
body text Notification body
notification_type string Type: task_assigned, item_update, mention, etc.
reference_type string Entity type referenced
reference_id UUID Entity ID referenced
read_at timestamp When the user read it (nullable)
created_at timestamp

13. Planning and execution scoring

13.1 canonical_plan

Reusable plan definitions for agent reasoning. Plans codify problem-solving approaches that agents can evaluate and select based on effectiveness history.

Field Type Description
id UUID PK
name string Plan name
description text What this plan does
plan_type string Category: research, analysis, coordination, etc.
steps JSONB Ordered step definitions
scope enum platform, account, agent
scope_id UUID FK to agent or account (for scoped plans)
created_at timestamp

13.2 execution_outcome

Recorded outcomes of plan executions, providing the raw data for effectiveness scoring.

Field Type Description
id UUID PK
canonical_plan_id UUID FK to canonical_plan
agent_id UUID FK to agent
interaction_id UUID FK to interaction
success boolean Whether the execution succeeded
cost decimal Execution cost
duration_ms int Execution time in milliseconds
context_hash string Hash of execution context for grouping similar executions
created_at timestamp

13.3 plan_score

Bayesian scores for plan effectiveness. Recalculated as new execution outcomes are recorded.

Field Type Description
id UUID PK
canonical_plan_id UUID FK to canonical_plan
agent_id UUID FK to agent
context_hash string Hash of execution context
score float Effectiveness score (0.0 to 1.0)
sample_count int Number of executions scored
last_updated timestamp When the score was last recalculated

14. Platform services

14.1 platform_service

External service registry. Tracks all third-party services the platform integrates with.

Field Type Description
id UUID PK
slug string Unique identifier (e.g. openai, anthropic, github)
name string Display name
description text What this service is
service_type enum llm, integration, external_tool
base_url string Service base URL
status enum active, disabled
created_at timestamp

14.2 llm_model

Available LLM models with pricing.

Field Type Description
id UUID PK
platform_service_id UUID FK to platform_service
slug string Model identifier (e.g. claude-3-opus)
name string Display name
provider string Provider name
input_cost_per_1k decimal Cost per 1000 input tokens (in account currency)
output_cost_per_1k decimal Cost per 1000 output tokens
context_window int Maximum context window in tokens
capabilities JSONB Model capabilities: vision, tool_use, etc.
status enum active, deprecated, disabled
created_at timestamp

14.3 account_service_config

Account-level service configuration overrides. Allows accounts to bring their own API keys.

Field Type Description
id UUID PK
account_id UUID FK to account
platform_service_id UUID FK to platform_service
api_key_ref string Secrets vault reference for API key
config JSONB Account-specific configuration
created_at timestamp

Rules:

  • Allows accounts to use their own API keys with external services.
  • api_key_ref points to an encrypted vault entry; keys are never stored directly in this table.

14.4 account_llm_preference

Account-level LLM model preferences by performance tier.

Field Type Description
id UUID PK
account_id UUID FK to account
tier enum fast, balanced, powerful
llm_model_id UUID FK to llm_model
created_at timestamp

Rules:

  • Each account specifies a preferred model for each performance tier.
  • Agents use these preferences unless they override at the agent level.

15. Media, storage and libraries

These entities support file storage, processing, and document-grounded knowledge retrieval. They are distinct from the knowledge_fact layer: facts are small, structured, accumulated through interaction; media-derived content is large, document-sourced, loaded deliberately by operators and agents. For the full ingestion pipeline see doc 05 Persistence, Storage & Ingestion.

15.1 storage_bucket

A registry of all storage buckets available to the platform, covering three tiers: Thinklio-managed shared buckets, Thinklio-managed dedicated enterprise buckets, and account-supplied BYOB (bring your own bucket) buckets.

Field Type Description
id UUID PK
name string Internal name (e.g. thinklio-au-01, acme-corp-r2)
bucket_type enum platform_shared, enterprise_dedicated, account_supplied
provider string r2, s3, minio, gcs: any S3-compatible provider
endpoint string S3-compatible endpoint URL
bucket_name string Bucket name at the provider
jurisdiction string AU, EU, US, OTHER: declared data residency region
account_id UUID FK to account; null for platform_shared; set for dedicated and account-supplied
credentials_ref string Reference to secrets vault entry; null for platform and enterprise buckets (credentials held by Thinklio ops)
validation_status enum pending, valid, invalid: result of last connectivity test
validated_at timestamp Timestamp of last successful validation
is_active boolean Whether this bucket accepts new uploads
created_at timestamp

Bucket types:

  • platform_shared: Managed by Thinklio operations; available to all accounts; credentials held in platform infrastructure. Thinklio guarantees data residency within the declared jurisdiction.
  • enterprise_dedicated: Managed by Thinklio operations; provisioned exclusively for one enterprise account. Used where dedicated infrastructure is required without the account managing the bucket themselves.
  • account_supplied: Registered by the account admin. Any S3-compatible provider. Credentials encrypted into the platform secrets vault at registration; credentials_ref is the vault lookup key. Thinklio accepts the account's jurisdiction declaration on trust.

Credential security: Access key and secret key for account-supplied buckets are never stored in this table. They are stored in the secrets vault; credentials_ref is the only field written here.

Rules:

  • At account creation, the system assigns the nearest-jurisdiction active platform_shared bucket as the default.
  • Enterprise customers may have one or more enterprise_dedicated buckets assigned by Thinklio ops.
  • Any account can register one or more account_supplied buckets via account settings; each undergoes a validation job (PUT/GET/DELETE test) before becoming active.
  • Bucket selection at upload time uses the account's current default bucket; no silent fallback between buckets.

15.2 account_storage_bucket

Links accounts to storage buckets. An account can have multiple active buckets; exactly one is the default for new uploads at any time.

Field Type Description
id UUID PK
account_id UUID FK to account
bucket_id UUID FK to storage_bucket
is_default boolean Whether this is the account's active default bucket
assigned_by enum system (auto at account creation), ops (Thinklio operations), account_admin (account-registered BYOB)
assigned_at timestamp

Rules:

  • Exactly one default bucket per account at any time (enforced by partial unique index).
  • system assignment happens at account creation; ops assignment is for enterprise-dedicated buckets; account_admin assignment follows a successful BYOB validation.
  • Changing the default does not migrate existing media. New uploads go to the new default; existing media stays in its original bucket (each media record carries bucket_id).

15.3 media

The central entity for all files stored in Thinklio. Any file uploaded by a user or created as an artefact by an agent is a media record. Media is a general-purpose service: agent knowledge ingestion, report outputs, user uploads, and agent-generated documents all use the same entity.

Field Type Description
id UUID PK
account_id UUID FK to account (nullable for personal or platform media)
scope enum agent, account, team, user
scope_id UUID FK to scope entity
agent_id UUID FK to agent (nullable: not all media is agent-related)
uploaded_by UUID FK to user_profile
original_filename string Filename as provided at upload or creation
stored_filename string UUID-based filename used in the bucket (avoids collisions and path traversal)
content_type string MIME type
file_size bigint Size in bytes
content_hash string SHA-256 hash for deduplication and integrity verification
bucket_id UUID FK to storage_bucket
bucket_key string Full object key/path within the bucket
status enum pending, processing, ready, failed, archived
keywords text[] Populated by enrichment processors
metadata JSONB Variable; populated by processors (image analysis results, sentiment, content flags, etc.)
summary text Nullable; plain-text summary generated by the summarise processor (capped at ~300 words)
summary_embedding vector(1536) Nullable; embedding of summary for lightweight single-vector retrieval
linked_type text Polymorphic entity type if linked: task, contact, item, note, etc.
linked_id UUID ID of the linked entity (nullable)
created_at timestamp
updated_at timestamp

Processing tiers:

  • Level 1 (automatic): The media record itself: filename, size, type, hash, bucket location. Created for every upload.
  • Level 2 (enrichment): Optional processors governed by account/agent rules. Outputs go to metadata, keywords, summary, and summary_embedding. Includes image analysis, content moderation, PDF text extraction, and summarisation.
  • Level 3 (full indexing): Chunking, embedding, and optional fact extraction. Creates library_item records. Triggered explicitly by intent at upload time, by processing rules, or by a recommended_for_indexing signal from the summarise processor.

Rules:

  • The original file in the bucket is immutable. All processing is derived and re-derivable from the stored blob.
  • content_hash enables deduplication: if the same hash already exists within scope, reference the existing blob rather than storing a duplicate.
  • summary and summary_embedding together enable useful retrieval without full indexing. Any summarised document is discoverable via semantic search even if never chunked.
  • Deleting a media record removes the bucket object, all derived library_item records, and any knowledge_fact records with source_media_id pointing to this record.
  • Media scope follows the same visibility rules as knowledge_fact.
  • linked_type and linked_id enable polymorphic attachment to entities. A user can attach a document to a task, item, contact, or note.

15.4 media_processor

Platform-registered processor definitions. Each processor is a discrete, independently runnable unit of work that can be applied to a media record. Processors are managed by Thinklio; accounts and users select which processors run via rules, but cannot define new processor types.

Field Type Description
id UUID PK
slug string Unique identifier: image_analysis, pdf_extract, summarise, chunk_and_index, content_moderation, virus_scan
name string Display name
description text What this processor does and what it outputs
applicable_mime_types text[] MIME type patterns: application/pdf, image/*, *
processing_tier enum level_2_enrichment or level_3_indexing
output_targets text[] Where output is written: metadata, keywords, summary, summary_embedding, library_items, knowledge_facts
depends_on text[] Slugs of processors that must complete successfully before this one can run
config_schema JSONB JSON Schema for processor-specific configuration options
status enum active, deprecated
version string Processor version
created_at timestamp

Rules:

  • depends_on is evaluated at job dispatch time. A processor whose dependencies have not completed is held in pending until they do.
  • processing_tier determines which governance layer controls enablement: Level 2 processors are available by default subject to account policy; Level 3 processors require explicit intent or a qualifying rule.
  • Processors with output_targets including summary or summary_embedding must only run against text-extractable content types.

15.5 media_processing_rule

Governs which processors run on which media, under what conditions. Rules are evaluated when a media record is created or reprocessed. The three-tier governance model mirrors the account/team/agent structure.

Field Type Description
id UUID PK
scope enum platform, account, agent
scope_id UUID FK to scope entity (null for platform rules)
processor_id UUID FK to media_processor
name string Human-readable rule name
mime_type_pattern string MIME type glob pattern this rule applies to
conditions JSONB Additional trigger conditions (e.g. file_size_min, recommended_for_indexing: true)
processor_config JSONB Configuration passed to the processor (e.g. summary_max_words: 300, target_library_id)
priority int Evaluation order; lower numbers are evaluated first
status enum active, disabled
created_at timestamp
updated_at timestamp

Rules:

  • Platform rules apply to all accounts unless overridden at account or agent scope.
  • Account rules apply to all agents within that account.
  • Agent rules apply only to that specific agent.
  • Rules can only narrow processor usage at lower scopes. An account cannot enable a processor that platform governance has disabled.
  • The conditions field supports recommended_for_indexing: true as a trigger condition, enabling the summarise processor to signal Level 3 readiness and have a chunking rule fire automatically, subject to other conditions and governance approval.

15.6 media_processing_job

Tracks each processor run against each media record. One record per processor per media file, updated on retry.

Field Type Description
id UUID PK
media_id UUID FK to media
processor_id UUID FK to media_processor
rule_id UUID FK to media_processing_rule (nullable for manually triggered runs)
status enum pending, running, completed, failed, skipped
input_snapshot JSONB Parameters passed to the processor
output JSONB Processor result prior to writing to output targets
error_data JSONB Failure reason and details
started_at timestamp
completed_at timestamp
created_at timestamp

Rules:

  • At most one active (pending or running) job per processor per media record.
  • Failed jobs can be retried; the existing record is updated, not replaced.
  • skipped indicates that the processor's MIME type pattern or conditions were not met, or a dependency failed.
  • Output is retained for audit, debugging, and reprocessing purposes.

15.7 library

A named, curated collection of media-derived content available for semantic retrieval. Libraries are the primary knowledge corpus for agents that depend on document-grounded responses (Coach Agent, HR Agent, Knowledge Base Agent, and others).

Field Type Description
id UUID PK
name string Display name (e.g. "General Coaching Methodology", "Acme Corp HR Policies")
description text What this library contains and what it is for
scope enum platform (Thinklio-managed, reusable across all accounts) or account (customer-owned)
account_id UUID FK to account (null for platform libraries)
embedding_model string Embedding model used for all items in this library. Must be consistent for valid similarity search.
chunk_strategy JSONB Chunking configuration: target_tokens, overlap_tokens, boundary_strategy
status enum active, archived
created_by UUID FK to user_profile
created_at timestamp
updated_at timestamp

Rules:

  • Platform libraries are Thinklio-managed and available to all accounts, useful for reusable domain corpora (general coaching methodology, standard HR frameworks, etc.).
  • Account libraries are customer-owned and visible only within that account.
  • All items in a library must use the same embedding model. Changing the model requires reprocessing all items.
  • Archiving a library prevents new items from being added but does not remove existing items; agents connected to an archived library can still retrieve from it.

15.8 library_item

A chunk of content from a media file, stored with an embedding for semantic retrieval. The atomic unit of library knowledge.

Field Type Description
id UUID PK
library_id UUID FK to library
media_id UUID FK to media (the source file)
chunk_index int Position of this chunk within the source document
content text The chunk text
token_count int Approximate token count
embedding vector(1536) Semantic embedding for similarity search
metadata JSONB Source location (page number, section heading, etc.) and other attributes
created_at timestamp

Rules:

  • Created by the chunk_and_index processor; not manually inserted.
  • If the source media record is deleted, all derived library_item records are cascade-deleted.
  • Reprocessing a media file within a library deletes and recreates its items for that library.
  • A media file can contribute items to multiple libraries (a policy document added to both the account library and a specialist compliance library, for example).

15.9 agent_library

Links agents to libraries, governing which libraries are searched during context assembly.

Field Type Description
id UUID PK
agent_id UUID FK to agent
library_id UUID FK to library
priority int Search order when multiple libraries are attached. Lower number = higher priority.
created_at timestamp

Rules:

  • An agent can be connected to multiple libraries.
  • Priority determines search order in context assembly. Higher-priority library results are retrieved first and weighted higher when the token budget forces truncation.
  • agent_catalog.library_assignments pre-configures these associations; when an agent is deployed from a template the library connections are created automatically.

16. Access and security

For the full security model, secrets vault design, and governance policy framework see doc 07.

16.1 invitation

Account and team invitations.

Field Type Description
id UUID PK
account_id UUID FK to account
team_id UUID FK to team (nullable)
invited_email string Email address being invited
invited_by UUID FK to user_profile
role string Role being offered: owner, admin, editor, viewer
token string One-time invitation token
status enum pending, accepted, expired, revoked
expires_at timestamp When the invitation expires
created_at timestamp

16.2 api_key

API keys for programmatic access.

Field Type Description
id UUID PK
account_id UUID FK to account
name string Key name for identification
key_hash string Hashed key (never store plaintext)
scope JSONB Allowed endpoints and methods
status enum active, revoked
last_used_at timestamp When the key was last used
created_by UUID FK to user_profile
created_at timestamp

16.3 oauth_token

OAuth tokens for external service integrations.

Field Type Description
id UUID PK
account_id UUID FK to account
platform_service_id UUID FK to platform_service
access_token_ref string Vault reference (never stored plaintext)
refresh_token_ref string Vault reference (nullable)
expires_at timestamp Token expiration
created_at timestamp
updated_at timestamp Last refresh

16.4 webhook_subscription

Webhook subscription configurations for outbound event delivery.

Field Type Description
id UUID PK
account_id UUID FK to account
url string Webhook URL
events text[] Event types to subscribe to
secret_ref string Vault reference for webhook signing secret
status enum active, disabled
created_at timestamp

17. Billing and resource management

17.1 credit_ledger

Credit transaction log. Every credit and debit is recorded as an immutable ledger entry.

Field Type Description
id UUID PK
account_id UUID FK to account
amount decimal Credit amount (positive for credits, negative for usage)
balance_after decimal Running balance after transaction
description string Reason for transaction
reference_type string Type of entity referenced: interaction, job, etc.
reference_id UUID ID of referenced entity
created_at timestamp Transaction date

17.2 budget_limit

Budget limits per scope.

Field Type Description
id UUID PK
account_id UUID FK to account
scope enum account, team, user
scope_id UUID FK to team or user_profile (nullable for account scope)
monthly_limit decimal Monthly spending limit
current_spend decimal Current month's spending
period_start timestamp Start of current billing period
created_at timestamp
updated_at timestamp

17.3 usage_record

Resource consumption records. One record per billable unit of work.

Field Type Description
id UUID PK
interaction_id UUID FK to interaction (nullable)
step_id UUID FK to step (nullable)
agent_id UUID FK to agent
user_id UUID FK to user_profile
team_id UUID FK to team (nullable)
account_id UUID FK to account
cost decimal Cost of this usage
cost_detail JSONB Breakdown: tokens, API calls, duration, etc.
created_at timestamp

17.4 quality_rating

User feedback ratings on agent interactions.

Field Type Description
id UUID PK
interaction_id UUID FK to interaction
user_id UUID FK to user_profile
rating int Score from 1 to 5
comment text Optional feedback comment
created_at timestamp

17.5 platform_config

Global platform configuration (singleton).

Field Type Description
id UUID PK
config JSONB Platform configuration: feature flags, defaults, limits, etc.
updated_at timestamp Last update

18. Agent tool integration

The core data structures are exposed as native tools in the tool registry. Every agent can use them based on its tool assignments.

18.1 Task tools

Tool slug Type Trust level Description
native_task_create internal low_risk_write Create a task (optionally in a list)
native_task_update internal low_risk_write Update status, priority, assignment, due date
native_task_list internal read List tasks with filters (status, assigned_to, due_date, list)
native_task_search internal read Search tasks by keyword
native_task_complete internal low_risk_write Mark task as done

18.2 Contact tools

Tool slug Type Trust level Description
native_contact_create internal low_risk_write Create a person or organisation
native_contact_update internal low_risk_write Update contact details
native_contact_search internal read Search contacts by name, email, organisation
native_contact_log internal low_risk_write Log an interaction with a contact
native_contact_history internal read Get interaction history for a contact

18.3 Item tools

Tool slug Type Trust level Description
native_item_create internal low_risk_write Create a ticket/request/inquiry
native_item_update internal low_risk_write Update status, assignment, priority
native_item_list internal read List items with filters (type, status, assigned)
native_item_assign internal low_risk_write Assign to user, team, or agent

18.4 Note tools

Tool slug Type Trust level Description
native_note_create internal low_risk_write Create a note, optionally linked to an entity
native_note_search internal read Search notes by keyword

18.5 Tag tools

Tool slug Type Trust level Description
native_tag_apply internal low_risk_write Apply a tag to any entity
native_tag_remove internal low_risk_write Remove a tag from an entity

18.6 Relationship to external integration tools

The existing external tools (tasks_create, tasks_list, crm_search_contacts, etc.) continue to work with external providers (Todoist, HubSpot). The native_* tools operate on the platform's own data.

Accounts can configure sync between native structures and external services:

  • Todoist sync: native tasks to/from Todoist tasks (two-way).
  • HubSpot sync: native contacts to/from HubSpot contacts (two-way).

When sync is active, both the native tool and the external tool write to the same underlying data, with conflict resolution based on last-modified timestamp.

When sync is not active, the native and external tools operate independently. An agent can use native_task_create for internal tasks and tasks_create (Todoist) for externally-managed tasks.

19. API endpoints

All endpoints require authentication and respect scoping rules. In the legacy model, authentication uses Supabase JWT. In the Convex model, authentication uses Clerk session tokens validated by Convex middleware.

19.1 Tasks

GET    /v1/tasks?scope=user&scope_id=...&status=todo&list_id=...
POST   /v1/tasks                        Create task
GET    /v1/tasks/{id}                   Get task
PATCH  /v1/tasks/{id}                   Update task
DELETE /v1/tasks/{id}                   Delete task

GET    /v1/task-lists?scope=user&scope_id=...
POST   /v1/task-lists                   Create list
PATCH  /v1/task-lists/{id}              Update list
DELETE /v1/task-lists/{id}              Archive list

19.2 Task types

GET    /v1/task-types?account_id=...    List task types (system + account-custom)
POST   /v1/task-types                   Create custom task type (admin only)
GET    /v1/task-types/{id}              Get task type
PATCH  /v1/task-types/{id}              Update custom task type (admin only, system types immutable)
DELETE /v1/task-types/{id}              Delete custom task type (admin only, system types immutable)

19.3 Contacts

GET    /v1/contacts?scope=account&scope_id=...&type=person&q=search
POST   /v1/contacts                     Create contact
GET    /v1/contacts/{id}                Get contact with recent interactions
PATCH  /v1/contacts/{id}                Update contact
DELETE /v1/contacts/{id}                Delete contact

POST   /v1/contacts/{id}/interactions   Log an interaction
GET    /v1/contacts/{id}/interactions   List interactions

19.4 Items

GET    /v1/items?scope=account&scope_id=...&type=support_ticket&status=open
POST   /v1/items                        Create item
GET    /v1/items/{id}                   Get item with linked tasks
PATCH  /v1/items/{id}                   Update item
POST   /v1/items/{id}/assign            Assign to user/team/agent

19.5 Item types

GET    /v1/item-types?account_id=...    List item types (system + account-custom)
POST   /v1/item-types                   Create custom item type
GET    /v1/item-types/{id}              Get item type
PATCH  /v1/item-types/{id}              Update custom item type
DELETE /v1/item-types/{id}              Delete custom item type

19.6 Notes

GET    /v1/notes?scope=user&scope_id=...&linked_type=contact&linked_id=...
POST   /v1/notes                        Create note
GET    /v1/notes/{id}                   Get note
PATCH  /v1/notes/{id}                   Update note
DELETE /v1/notes/{id}                   Delete note

POST   /v1/notes/{id}/share             Share note with user or team
DELETE /v1/notes/{id}/share/{share_id}  Revoke a share
GET    /v1/notes/{id}/mentions          List mentions in a note

19.7 Note types

GET    /v1/note-types?account_id=...    List note types (system + account-custom)
POST   /v1/note-types                   Create custom note type
GET    /v1/note-types/{id}              Get note type
PATCH  /v1/note-types/{id}              Update custom note type
DELETE /v1/note-types/{id}              Delete custom note type

19.8 Mentions

GET    /v1/mentions/search?q=...&types=user,contact,task

Returns matching entities across users, contacts, tasks, items, and agents. Used by the UI for autocomplete when typing @ in a note. Results are scoped to the caller's visibility.

19.9 Tags

GET    /v1/tags?account_id=...          List account's tags
POST   /v1/tags                         Create tag
POST   /v1/tags/apply                   { tag_id, entity_type, entity_id }
POST   /v1/tags/remove                  { tag_id, entity_type, entity_id }

19.10 Agent events

POST   /v1/agent-events                 Push a structured event to an agent

The POST /v1/agent-events endpoint provides bidirectional agent communication. External systems and agents can push structured events into the platform, and the platform routes them to the appropriate agent for processing.

Event structure:

{
  "agent_id": "uuid",
  "event_type": "deployment.completed",
  "payload": { ... },
  "source": "github-actions",
  "correlation_id": "optional-tracking-id"
}

Processing: The gateway receives the event, validates the agent exists and the caller has permission, then routes it through the standard event bus. The agent processes it as a non-conversational interaction: the agent can take action (create tasks, update items, notify users via user_comm) without requiring a user conversation.

Use cases: CI/CD pipeline notifies a DevOps agent of deployment status. Monitoring system alerts an ops agent of threshold breaches. External CRM pushes contact updates to a CRM agent. Scheduled cron triggers a daily digest agent.

20. Agent usage patterns

The following scenarios illustrate how agents use the core data structures in combination.

20.1 Personal assistant

The canonical use case. A personal assistant agent uses all four structures:

  • Tasks: "Remind me to call John tomorrow" creates a task via native_task_create with due_date.
  • Contacts: "I just met Sarah from Acme at the conference" creates a contact via native_contact_create and logs the meeting via native_contact_log.
  • Notes: "Here are my notes from today's meeting" creates a note via native_note_create linked to attendee contacts.
  • Items: Less common, but "I got a request from the finance team" creates an item via native_item_create.

Daily briefing: the agent queries tasks due today, overdue items, recent contact interactions, and surfaces them in the briefing page.

20.2 Support agent

Primarily works with items and contacts:

  • Inbound email triggers the agent to create an item of type support_ticket with source = 'email' and link to the contact.
  • Agent triages: sets priority, assigns to team or user.
  • Assignment creates a task for the assignee: "Investigate billing issue for Acme".
  • Agent logs interactions: "Replied to customer with workaround".
  • Resolution: item transitions through in_progress to resolved to closed.

20.3 CRM agent

Primarily works with contacts and contact interactions:

  • "Log a call with John at Acme, discussed renewal, he's interested in the enterprise plan."
  • Agent creates a contact interaction, updates contact metadata with deal context.
  • Agent can query: "Who haven't we spoken to in 30 days?" via native_contact_search with last interaction filter.
  • Syncs with HubSpot if configured.

20.4 Task management agent

The dedicated task organiser:

  • Creates and manages task lists: "Create a list for the product launch."
  • Reprioritises: "What's the most important thing I should work on today?"
  • Reports: "Give me a summary of what the team completed this week."
  • Reminders: surfaces overdue tasks and approaching deadlines.
  • Reorganisation: move tasks between lists, batch-update priorities.

20.5 HR / onboarding agent

Uses items for requests and tasks for onboarding workflows:

  • New hire notification creates an item of type request.
  • Generates a task list "Onboarding: Jane Smith" with steps (IT setup, badge, training, etc.).
  • Tracks progress, reminds responsible parties, reports to HR admin.

20.6 Meeting agent

Primarily creates notes and tasks:

  • Post-meeting: agent creates a note with meeting summary linked to attendee contacts.
  • Extracts action items and creates tasks assigned to the responsible people.
  • Logs the meeting as a contact interaction for each attendee.

20.7 Custom and external agents

Third-party agents built on the Thinklio platform use the same tools:

  • A custom "Legal Review" agent creates items of type approval and tracks document review tasks.
  • A custom "Inventory" agent uses tasks to track restock actions and contacts to manage supplier relationships.
  • An external integration creates items via the REST API when events occur in their system.

The tool and API interfaces are identical for built-in and custom agents. No special access or different data model is needed.

21. Data lifecycle

21.1 Retention policies

Data type Default retention Configurable Archive strategy
Events 90 days active, then archived Per account Move to archive table
Interactions 90 days active Per account Move to archive table
Steps 90 days active Per account Move to archive table
Jobs 90 days active (from terminal state) Per account Move to archive table
Subjobs 90 days active (follows parent job) Per account Move to archive table
Tasks Indefinite (unless deleted) Per account Soft delete with TTL
Contacts Indefinite (unless deleted) Per account Soft delete with TTL
Items Indefinite (unless deleted) Per account Soft delete with TTL
Notes Indefinite (unless deleted) Per account Soft delete with TTL
Knowledge facts Indefinite Per account Soft delete with TTL
Audit logs 1 year Per account (min 90 days) Cold storage
User data Until deletion request N/A Hard delete with compliance log

21.2 Data deletion (GDPR right to erasure)

When a user requests deletion:

  • User knowledge facts: hard deleted.
  • User's messages in events: anonymised (content replaced, user_id nullified).
  • User's interactions and steps: anonymised.
  • User's job observer registrations: removed.
  • Team knowledge contributions: remain, attributed to "former member".
  • User's tasks, items, notes: marked as deleted and retained per retention policy.
  • Deletion logged in compliance audit trail.

21.3 Sync architecture (future)

For accounts that use external services alongside native structures:

  • Native task to/from Todoist task.
  • Native contact to/from HubSpot contact.
  • Native item to/from Zendesk ticket.

Sync principles:

  • Last-write-wins with configurable conflict resolution.
  • Sync is per-account, per-service, opt-in.
  • External ID stored in source_ref to prevent duplicates.
  • Sync runs on a schedule (polling) or via webhooks (push).
  • Sync failures are logged but do not block the native operation.

Implementation: A sync worker subscribes to change events on native entities and pushes to the external service. Inbound changes arrive via webhooks or polling and update the native entity. The metadata field stores sync state (last sync timestamp, external version).

This is a Phase 2+ feature. Phase 1 operates native and external independently.

22. Open questions

  • Convex schema mapping. The conceptual model uses UUIDs, enums, JSONB, and vector fields. Convex uses document IDs, string unions, validated objects, and vector indexes. A formal mapping document for the Convex schema may be useful as a companion to this conceptual model, or the conceptual model should be updated to use Convex-native types throughout once the migration is complete.
  • Type system extensibility. The system-reserved vs account-custom type pattern is consistent across task_type, item_type, and note_type. Should there be a single generic entity_type table with a domain discriminator (task, item, note), or is the per-domain table approach preferable for query simplicity and schema clarity?
  • Contact deduplication. With contacts arriving from multiple sources (manual, agent, import, integration), deduplication logic needs design. Options: exact email match, fuzzy name match with human confirmation, agent-assisted merge suggestions.
  • Note versioning. The current model tracks edited_at but not a full version history. Should note edits be versioned (git-style diffs, snapshot-based), and if so, what is the storage and query cost?
  • Media processing cost attribution. Level 2 and Level 3 media processing consume LLM tokens and compute. Should processing costs be attributed to the uploading user/agent, or spread across the account as infrastructure cost?
  • Tag hierarchy. The current tag model is flat. Should tags support hierarchy (parent/child) or namespacing for large accounts with many tags?

23. Revision history

Date Change
2026-03-14 Original Data Model (doc 05 v0.1.0) published
2026-03-21 Core Data Structures: Tasks, Contacts, Items & Notes (doc 32 v0.2.0) published
2026-03-21 Data Model updated to v0.3.0 incorporating doc 32 entities
2026-04-16 Consolidated docs 05 and 32 into this document (v1.0.0). All sources archived.