Documentación de Mentat — en evolución continua.
ReferenciaModelo de datos

Modelo de datos

Los tipos del modelo viven en packages/ontology-core (fuente única de verdad, TypeScript estricto). Esta página los resume. Todo cuelga de /organizations/{orgId}/… salvo /users (global).

Campos comunes: AuditFields (createdAt, updatedAt, createdBy), TenantFields (organizationId), SoftDeleteFields (deleted, deletedAt, deletedBy). Los logs inmutables (ActionExecution, IngestionRun) no llevan SoftDeleteFields.

OntologyType

/organizations/{orgId}/ontologyTypes/{typeId}

{
  id: string                 // doc id, inmutable
  displayName: string
  description: string
  icon: string               // nombre Lucide
  category: 'core' | 'derived' | 'use_case'
  properties: PropertyDefinition[]
  links: LinkDefinition[]
  version: number            // optimistic locking
  // + AuditFields, TenantFields, SoftDeleteFields
}
 
type PropertyDefinition =
  | { name; displayName; required; indexed; type: 'string'|'number'|'boolean'|'date'|'datetime' }
  | { …; type: 'enum'; enumValues: string[] }
  | { …; type: 'reference'; referenceType: string }
 
interface LinkDefinition {
  name; displayName; targetType: string
  cardinality: 'one_to_one' | 'one_to_many' | 'many_to_many'
  inverse?: string
}

MentatObject

/organizations/{orgId}/objects/{objectId} — colección plana discriminada por typeId.

{
  id: string                 // UUID
  typeId: string
  properties: Record<string, PrimitiveValue>
  links: Record<string, string | string[]>
  metadata: {
    createdAt; updatedAt; createdBy; version: number
    dataSourceId?: string    // si vino de una ingesta
    externalKey?: string     // "{dataSourceId}:{clave}" para upsert
  }
  // + deleted? (soft delete)
}

ActionType

/organizations/{orgId}/actionTypes/{id}

{
  id; displayName; description
  affectedTypes: string[]
  parameters: ActionParameter[]   // type: string|text|number|boolean|enum|reference
  rules: ActionRule[]             // modify | create | delete
  validations: ActionValidation[] // permission | stateCheck
  sideEffects: ActionSideEffect[] // notification | webhook
  permissions: { roles: UserRole[] }
  version: number
}
 
type ChangeSpec = { from: 'literal'; value } | { from: 'parameter'; parameter: string }
type TargetSelector = { from: 'parameter'; parameter: string }   // MVP: solo por parámetro reference
interface StateCheckConfig { target: TargetSelector; property: string;
  operator: 'eq'|'neq'|'in'|'notIn'|'gt'|'gte'|'lt'|'lte'|'exists'|'notExists'; value? }

ActionExecution (inmutable)

/organizations/{orgId}/actionExecutions/{id}

{
  id; actionTypeId; executedBy; executedAt
  parameters: Record<string, PrimitiveValue>
  affectedObjects: string[]
  status: 'success' | 'failed' | 'partial'
  error?: string
  sideEffectsExecuted: { type; status; details? }[]
  durationMs: number
}

DataSource

/organizations/{orgId}/dataSources/{id}

{
  id; name; description?
  source: { type: 'csv_upload'|'rest_api'|'webhook'|'manual'; config: … }
  mappings: { sourceField; targetType; targetProperty; transformation?; isIdentity? }[]
  schedule?: string          // cron, solo rest_api
  lastRun?; lastRunStatus?
  version: number
}

Secretos fuera del doc: /dataSourceSecrets/{id} (API keys; deny-all) y el tokenHash del webhook.

IngestionRun (inmutable)

/organizations/{orgId}/ingestionRuns/{id}

{
  id; dataSourceId; triggeredBy: userId|'scheduler'|'webhook'
  startedAt; finishedAt; durationMs
  status: 'success'|'partial'|'failed'
  counts: { read; created; updated; failed }
  errors: { row; message }[]   // acotado
  error?: string
}

SavedView

/organizations/{orgId}/savedViews/{id} — privada por owner.

{
  id; owner: userId; name; typeId
  filters: { property; operator: FilterOperator; value? }[]
  sort?: { property; direction: 'asc'|'desc' }
  columns?: string[]
  // + AuditFields, TenantFields, SoftDeleteFields (sin version)
}

AuditEvent (inmutable)

/organizations/{orgId}/auditEvents/{id} — línea de tiempo unificada.

{
  id; organizationId
  domain: 'ontology' | 'action' | 'user' | 'datasource'
  op: string                 // ej. "action.execution.complete"
  actor: userId; occurredAt
  target: { kind; id; context? }
  versions?: { prev?; next }
  diff?; metadata?
}

MentatUser

/users/{userId} (global).

{
  id; email; displayName
  organizationMemberships: { organizationId; role: UserRole; joinedAt }[]
  whitelistStatus: 'invited' | 'active' | 'rejected'
  metadata: { createdAt; lastLoginAt }
}

UserRole = 'admin' | 'editor' | 'operator' | 'viewer'. La membership efectiva para autorización se lee de /organizations/{orgId}/members/{uid}.